Total Pageviews

2017/03/08

[Design Pattern] Command Pattern

在 97 Things Every Programmer Should Know 一書中提到:
雖然在一般遇到的案例裡,使用 if - then - else 比多型更實用些,但絕大多數用 polymorphism 的寫作風格會讓程式碼更精簡、更易讀,且更少脆弱的程式碼。所以在我們的程式碼裡,要是錯過使用 polymorphism 的機會,便會逐漸增加  if - then - else 敘述句的數量。試試 command  pattern 吧

假設我有一個機器人物件,他會執行前進、後退、左轉與右轉,這四種指令

package albert.practice.designpattern.command;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@AllArgsConstructor
@Data
@Slf4j
public class Robot {

    private String robotName;

    public void goAhead() {
        log.debug(robotName + " 前進 !");
    }

    public void turnLeft() {
        log.debug(robotName + " 左轉 !");
    }

    public void turnRight() {
        log.debug(robotName + " 右轉 !");
    }

    public void turnBack() {
        log.debug(robotName + " 後退 !");
    }
}


另外建立一個 enumeration 來判斷,此次呼叫要執行哪一個指令
package albert.practice.designpattern.command;

public enum CommandEnum {
    GO_HEAD, TURN_BACK, TURN_RIGHT, TURN_LEFT;
}


建立機器人物件與執行指令如下:
package albert.practice.designpattern.command;

public class RobotTestClient {

    public static void main(String[] args) {
        Robot robot = new Robot("瓦力");

        RobotTestClient client = new RobotTestClient();
        client.executeRobot(robot, CommandEnum.GO_HEAD);
        client.executeRobot(robot, CommandEnum.TURN_LEFT);
        client.executeRobot(robot, CommandEnum.TURN_RIGHT);        
    }

    public void executeRobot(Robot robot, CommandEnum command) {
        if (CommandEnum.GO_HEAD == command) {
            robot.goAhead();
        } else if (CommandEnum.TURN_BACK == command) {
            robot.turnBack();
        } else if (CommandEnum.TURN_LEFT == command) {
            robot.turnLeft();
        } else if (CommandEnum.TURN_RIGHT == command) {
            robot.turnRight();
        }
    }

}



在上述的程式碼就會發現,如果指令越來越多,就會有越來越多的 if-else 判斷式,此時就可以套用 command pattern

首先,先建立 Command interface
package albert.practice.designpattern.command;

public interface Command {
    void execute();
}



然後,分別建立四個指令的 classes,並實作 Command interface
前進指令:
package albert.practice.designpattern.command;

public class GoAheadCommand implements Command {

    private Robot robot;

    public GoAheadCommand(Robot robot) {
        super();
        this.robot = robot;
    }

    @Override
    public void execute() {
        this.robot.goAhead();
    }

}


後退指令:
package albert.practice.designpattern.command;

public class TurnBackCommand implements Command {
    private Robot robot;

    public TurnBackCommand(Robot robot) {
        super();
        this.robot = robot;
    }

    @Override
    public void execute() {
        this.robot.turnBack();
    }

}



向左轉指令:

package albert.practice.designpattern.command;

public class TurnLeftCommand implements Command {
    private Robot robot;

    public TurnLeftCommand(Robot robot) {
        super();
        this.robot = robot;
    }

    @Override
    public void execute() {
        this.robot.turnLeft();
    }

}



向右轉指令:
package albert.practice.designpattern.command;

public class TurnRightCommand implements Command {
    private Robot robot;

    public TurnRightCommand(Robot robot) {
        super();
        this.robot = robot;
    }

    @Override
    public void execute() {
        this.robot.turnRight();
    }

}



建立機器人物件與操作指令,程式修改如下:

package albert.practice.designpattern.command;

public class RobotTestClient {

    public static void main(String[] args) {
        Robot robot = new Robot("瓦力");
        new GoAheadCommand(robot).execute();
        new TurnLeftCommand(robot).execute();
        new TurnRightCommand(robot).execute();
    }

}



2017/03/07

[Redmine] How to get mail notification value in user page

Problem
I am trying to get admin value from the redmine user object. 
But I cannot get admin value, no matter using Java API or Rest API.
It can only retrieve very limited information:



How-To
We can make good use of jsoup to parse html page and get value from the specific html element.

Firstly, checking the drop down list element in html. 


Secondly, using jsoup to parse HTML
1
2
3
4
5
6
7
8
9
    String url = "http://192.168.0.1/users/" + userId + "/edit";
    Document document = Jsoup.connect(url).get();
    Elements mailNotificationElements = document.select("[name=user[mail_notification]]").select("option");
    for (Element element : mailNotificationElements) {
        if ("selected".equals(element.attr("selected"))) {
            log.debug("the mail_notification value = " + element.val());
            break;
        }
    }


Reference
[1] https://jsoup.org/



2017/03/06

[Redmine] How to get admin value in user page

Problem
I am trying to get admin value from the redmine user object. 
But I cannot get admin value, no matter using Java API or Rest API.
It can only retrieve very limited information:



How-To
We can make good use of jsoup to parse html page and get value from the specific html element.

Firstly, checking the checkbox element in html. 
We can find out the element name and the checked checkbox will add checked="checked" attribute.

If the checkbox does not be checked, it will not have checked="checked" attribute.


Secondly, using jsoup to parse HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    String url = "http://192.168.0.1/users/" + userId + "/edit";
    // Connect to redmine url and get HTML document
    Document document = Jsoup.connect(url).get();
    
    // Find elements that match [name=user[admin]]
    Elements adminElements = document.select("[name=user[admin]]");
    for (Element element : adminElements) {
       // if the element has checked="checked" attribute, it means this user is administrator, 
       // or this user is a normal user
       if ("checked".equals(element.attr("checked"))) {
           log.debug("this user is admin");
           break;
       }
    }


Reference
[1] https://jsoup.org/

2017/03/05

[Angular2] Fail to apply bootstrap on checkbox with Angular2

Problem
According to the example in w3schools, http://www.w3schools.com/bootstrap/bootstrap_forms_inputs.asp

If the checkbox is static, it will not have this problem. But if the checkboxes generate dynamically, using *ngFor, the checkbox square is missing


The code snippet is as bellows:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<div class="row" *ngIf="roles">
   <div>
      <label>角色</label>
   </div>
   <div *ngFor="let role of roles" style="display: inline-block">
      <div class="checkbox">
         <label style="padding-right:10px; padding-left:10px" >
         <input type="checkbox" [(ngModel)]="role.isSelected" (change)="checkRole(role)">
         {{ role.name }}
         </label>
      </div>
   </div>
</div>


How-To
It seems that if the checkbox generate dynamically, it will lack of some html tags to assign class for unknown reasons. Therefore, you need to add these span tag manually (line 9).  

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<div class="row" *ngIf="roles">
   <div>
      <label>角色</label>
   </div>
   <div *ngFor="let role of roles" style="display: inline-block">
      <div class="checkbox">
         <label style="padding-right:10px; padding-left:10px" >
         <input type="checkbox" [(ngModel)]="role.isSelected" (change)="checkRole(role)">
         <span class="checkbox-material"><span class="check"></span></span>
         {{ role.name }}
         </label>
      </div>
   </div>
</div>




2017/03/04

[jackson] How to rename JSON attribute name

Problem
I would like to create a Java Bean to do mapping with JSON object.
But the JSON object has an attribute name, public, which has conflict with Java, because public is an reserved word in Java. How to handle this problem?



How-to
You can utilize @JsonProperty to rename it.
The Java bean looks like (publicStr is mapping to public in JSON):
    @Data
    @ToString
    public static class DsnDto {
        @JsonProperty("public")
        private String publicStr;
        private String secret;
        private String csp;
    }


Reference
[1] https://stackoverflow.com/questions/9741134/jackson-annotation-how-to-rename-element-names

2017/03/03

[liquibase] Caused by: liquibase.exception.LockException: Could not acquire change log lock

Problem

I am running a lot of liquibase-scripts on PostgreSQL database, and get error message as I startup Spring Boot:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
Waiting for changelog lock....
Waiting for changelog lock....
Waiting for changelog lock....
Waiting for changelog lock....
Waiting for changelog lock....
Waiting for changelog lock....
Caused by: liquibase.exception.LockException: Could not acquire change log lock.  Currently locked by albert-PC (10.11.22.33) since 2016/9/26 下午 2:06
 at liquibase.lockservice.StandardLockService.waitForLock(StandardLockService.java:190) ~[liquibase-core-3.5.1.jar:na]
 at liquibase.Liquibase.update(Liquibase.java:196) ~[liquibase-core-3.5.1.jar:na]
 at liquibase.Liquibase.update(Liquibase.java:192) ~[liquibase-core-3.5.1.jar:na]
 at liquibase.integration.spring.SpringLiquibase.performUpdate(SpringLiquibase.java:434) ~[liquibase-core-3.5.1.jar:na]
 at liquibase.integration.spring.SpringLiquibase.afterPropertiesSet(SpringLiquibase.java:391) ~[liquibase-core-3.5.1.jar:na]
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
 ... 160 common frames omitted


How-to
The problem was the buggy implementation of SequenceExists in Liquibase. Since the changesets with these statements took a very long time and was accidently aborted. Then the next try executing the liquibase-scripts the lock was held.
Therefore, you can execute this DML to update DATABASECHANGELOGLOCK table:

UPDATE DATABASECHANGELOGLOCK SET LOCKED=FALSE, LOCKGRANTED=null, LOCKEDBY=null where ID=1

Reference

[1] https://stackoverflow.com/questions/15528795/liquibase-lock-reasons

2017/03/02

[HTML] How to arrange multiple elements in the same row

Problem
In this dialog, I hope the drop down list should be at the right side of the radio button, not in the new line.




The code snippet looks like:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<div class="row">
   <div class="col-sm-12">
      <div class="form-group" style="display:inline">
         <label>即時應用系統異常追蹤整合</label>
         <div>
            <input type="radio" name="sentryOption" [checked]="newProject.sentryOption==='0'" (click)="newProject.sentryOption='0'">立即建立專案關聯
            <span style="padding-left:20px">
            <input type="radio" name="sentryOption" [checked]="newProject.sentryOption==='1'" (click)="newProject.sentryOption='1'">選擇既有的關聯
            </span>
            <span style="padding-left:5px">
               <select class="form-control" name="sentryProjectId" [(ngModel)]="newProject.sentryProjectId" [style.width.px]="50" *ngIf="newProject.sentryOption==='1'"
               >
               <option value="" selected> 請選擇 </option>
               <option *ngFor="let sp of sentryProjects" [ngValue]="sp.slug">{{sp.slug}}</option>
               </select>
            </span>
         </div>
      </div>
   </div>
</div>


How-to
You can use CSS to fulfill this requirement:
display:inline-block

Just add display:inline-block; to this drop down list's style, the updated code snippet is as bellowing:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<div class="row">
   <div class="col-sm-12">
      <div class="form-group" style="display:inline">
         <label>即時應用系統異常追蹤整合</label>
         <div>
            <input type="radio" name="sentryOption" [checked]="newProject.sentryOption==='0'" (click)="newProject.sentryOption='0'">立即建立專案關聯
            <span style="padding-left:20px">
            <input type="radio" name="sentryOption" [checked]="newProject.sentryOption==='1'" (click)="newProject.sentryOption='1'">選擇既有的關聯
            </span>
            <span style="padding-left:5px">
               <select class="form-control" name="sentryProjectId" [(ngModel)]="newProject.sentryProjectId" [style.width.px]="50" *ngIf="newProject.sentryOption==='1'"
               style="display:inline-block;">
               <option value="" selected> 請選擇 </option>
               <option *ngFor="let sp of sentryProjects" [ngValue]="sp.slug">{{sp.slug}}</option>
               </select>
            </span>
         </div>
      </div>
   </div>
</div>    


See...the radio buttons and drop down list are in the same row.

Reference
[1] https://www.quora.com/I-want-to-put-two-divs-in-HTML-in-the-same-line-but-one-always-comes-under-the-other-one-What-should-I-do
[2] http://crunchify.com/basic-html-how-do-you-create-blank-space-in-html/    

2017/03/01

[閱讀筆記] The World Is Flat (2/2)


  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. 今日油氣佔出口總額三成以上的國際,有34個低開發國,有1/3非民主國家,有12國的人均所得在1500美元以下
  26. 人會改變,一定是他注意到什麼,而不是別人說了什麼敎

2017/02/28

2017/02 Travel

南雅奇岩
DSC01236

DSC01241


基隆情人湖老鷹岩
DSC01284


嘉義北門車站
DSC01316

DSC01335


新港奉天宮
IMG_6077

2017/02/10

[Astah] How to Change Font Size in Astah?

Problem
How to change font size in Astah?

How-to
Go to Tool => Project => Default Font, you can change font, font style and font size in this dialog.





PS. Font configuration only allow in Professional version, Community version is disallowed.


2017/02/09

[Sentry] Fail to Create a New Project via REST API

Problem
I am using spring boot to implement web application, and trying to use sentry's rest API to create a new sentry project. I get this exception as I use rest API:
1
2
3
4
5
org.springframework.web.client.HttpClientErrorException: 403 OK
 at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
 at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:667) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
 at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:620) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
 at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:580) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]

I try to use Advanced REST Client to do test, it succeed. Why?



How-To
Owing this http request is triggered at server side, so we cannot use browser's developer tool to debug.

You can go to telerik to download fiddler to help us to do web debug and to figure out what's the response it returns to us.

Fiddler is a free web debugging proxy which logs all HTTP(s) traffic between your computer and the Internet. Use it to debug traffic from virtually any application that supports a proxy like IE, Chrome, Safari, Firefox, Opera and more.
Before using fiddler, you need to set proxy to spring boot to send web traffic to Fiddler.


You can see all requests in fiddler, it looks like:



We also see the bad request, and find out the problem results from the rest URL had unnecessary blank (%20)



The response looks like (sentry tell it violate CSRF problem, but the real problem is the wrong REST URL):



Reference
[1] https://docs.sentry.io/hosted/api/teams/post-team-project-index/

2017/02/08

The Bat and A Ball Problem

最近在看 Thinking, Fast and Slow 這本書,裡頭提到一個 The Bat and A Ball Problem,問題描述如下:
If a baseball and a bat cost $1.10 together, and the bat costs $1.00 more than the ball, how much does the ball cost?

根據作者的實驗,超過 80% 的大學生會回答 0.1 元,但是這個答案卻是錯的,因為題目是說,球棒比球多 1 元,沒有說球棒是 1 元,人習慣靠直覺 (System 1) 來快速得到答案,因為比較省時、省力,但是 System 1 很容易犯錯

以下是運算式,X 代表球的價錢,最後推導出 X = 0.05









回答 0.1 的人,比較偏向直覺思考者,即運用 System 1 的人;回答 0.05 的人,比較偏向理性思考者,即運用 System 2 的人。

兩種 system 有其本質的差異:
系統一 (System 1):是 automatic system,屬於自動化、反射性、快速、不費力的思考方式。如運算 1 + 1 等於多少、看著報紙念出一則報導、理解一個簡單的句子等。但是,System 1 會自動運作且無法隨心所欲地關閉它,所以我們無可避免的會產生直覺錯誤、認知幻覺,System 1 的偏誤是無法避免的
系統二 (System 2):是 effortful system,屬於較費力、需專注力、邏輯思考、牽涉複雜運算、推理的思考方式。如填寫稅單、比較兩隻手機的優缺點、把車子停在一個格子較小的停車格。但是,System 2 用來取代 System 1 來做日常的決策是不切實際的,因為它運作緩慢且沒效率,我們能做的是:學習識別我們所處的情境的犯錯機率,當風險很高時要盡量避免犯下重大錯誤