顯示具有 工作紀錄 標籤的文章。 顯示所有文章
顯示具有 工作紀錄 標籤的文章。 顯示所有文章

2022/05/08

意見溝通的策略


在TED上讀到這篇文章 6 tactics to help you turn heated dinner arguments into real conversations


  • 說出不同意見
    • 熱烈討論是強化信念的方式,也有助於破除盲點
    • 保持冷靜,不作人身攻擊
    • 言詞避用你我他,主詞儘量用我們
  • 釐清並對現況與議題建立共識
    • 避免雞同鴨講
    • 透過提問來釐清現況與問題,趨近出第一步的共識
    • 有助於雙方開放想法
  • 對事不對人
    • 討論是思想的競賽,不是攻擊他人
    • 每個人都是獨特的個體,不可一竿子打翻一船人
  • 營造可接受錯誤的討論平台
    • 營造更大更發散的空間,讓創新的想法得以孕育
    • 可讓討論者更開放心胸
    • 透過語言、語氣、肢體動作等來引導更發散的想法
  • 只使用可靠到事實
    • 假設與事實是不同的
    • 若要將一事視為事實,必須確認其來源、內容的可靠性
    • 若無法完全確認可信度,就應該稍作存疑
  • 適時停止
    • 感謝對方的意見、欣賞對方的論點,將發言權交回對方


討論過程必須有公正第三者來管理討論,隨時制止或中斷討論,避免陷入情緒,第三者不可有預設立場選邊站
別急著將自己的想法放進別人的腦子。聆聽,找出機會與契機,然後再行動

2019/01/16

以Python程式產生InfluxDB資料

我們接下來將寫個Python程式,來模擬溫度sensor產生資料並寫入InfluxDB中。然後利用這些資料,在Chronograf中設定dashboard的樣式。

要讓Python連上InfluxDB,我們需要Python-InfluxDB connection。我們可以使用pip來安裝這個connection元件:"pip install influxdb"。詳細指令介紹可參考這一頁。(此範例使用的是Python 3.x)

接下來的程式是模擬廚房、臥室、客廳三個溫度感應器,每秒鐘會回報溫度到InfluxDB。程式一開始,會先建立與InfluxDB server的連結,並建立一個 "sample"的database。我們準備了一個obj的json物件樣板。大家可以把measurement想像成表格(table)名稱。這裡有兩個columns,一個是location用來存放感應器位置,另一個是value來存放溫度。前面有提過,tag是indexed column,field是non-indexed column。由於InfluxDB是時間序列資料庫,因此每筆資料都隱含了一個time的column。

開始執行此Python程式後,就可以看到InfluxDB視窗一直有log message在跑,如果沒有error message,就表示此python程式正在餵資料到InfluxDB。




from influxdb import InfluxDBClient
import random
import time

host = "localhost" #influxdb IP
port = 8086 #influxdb port
username = "root" #influxdb username
password = "root" #influxdb password
dbname = "sample" #database name

# json object template
obj = {'measurement':'temperature', 'tags':{'location':''}, 'fields':{'value':0}}

# arrays for location names and temperatures
locations = ['kitchen', 'bedroom', 'living room']
t = [20] * len(locations)

client = InfluxDBClient(host, port, username, password) # connect influxdb
client.create_database(dbname) # create database

while True:
 for i in range(0,len(locations)):
  t[i] = round(t[i] + random.uniform(-0.5, 0.5), 2) # generate temperature

  # prepare data object
  obj['tags']['location'] = locations[i]
  obj['fields']['value'] = t[i]
  print(obj)

  # write data to influxdb
  client.write_points([obj], 'ms', dbname)

 time.sleep(1)


Python-InfluxDB connection元件的相關資料,可參考這頁的說明。

我們可以把InfluxDB client開啟,打入下列指令,來看最近三秒寫入InfluxDB的資料,確認資料確實有寫入。從這裡我們也可看到,除了location與value這兩個我們設定的欄位外,還有一個time欄位。

接著我們再用瀏覽器進入Chronograf網頁介面。(http://localhost:8888)
我們到Dashboard這頁,可以看到下面的畫面。

點選line,然後add data,接下來都可以用點選的方式來產生dashboard圖樣。我們先產生一個彙總的圖表。點選sample,再點選temperature,把三個地點都勾起來,並點選group by location,然後再把value勾起來。這個操作過程會自動幫我們產生InfluxQL。完成後點選右上角的綠色勾即可儲存。

回到Dashboard頁面,可以設定此圖表之位置與大小,並可在右上角設定更新速度與資料顯示長度。

正上方有Add a Cell to Dashboard藍色按鈕,可以加入更多圖表。大家可以去玩玩各種圖表,了解Chronograf。Chronograf的使用說明可以參考官網的文件


InfluxDB

若想了解時間序列資料,可參考這篇簡述
InfluxDB應該是目前佔有率最高的TSDB了,因此我們利用InfluxDB來了解TSDB的運作。

這裡是InfluxData的官網,其open source的版本中有一個TICK stack的架構,包含了四個元件:


  • Telegraf:資料收集元件
  • InfluxDB:資料儲存元件
  • Chronograf:視覺化操作介面
  • Kapacitor:資料處理引擎
這四個元件組合起來,對大部分的應用來說,應該是相當充足了。

為熟悉InfluxDB,我們透過關聯式資料庫的概念來了解一下InfluxDB的架構。你也可以透過官網的Key concepts來做更深入的了解。
  • database:這個概念跟關聯式資料庫一樣
  • measurement:相當於表格(table)
  • tag:有索引的欄位 (indexed column)
  • field:沒有索引的欄位 (non-indexed column)
先提這四個名詞。如果各位想了解更多詞彙,可以參考官網上的Glossary

為了快速達到了解InfluxDB,我們只先專注在InfluxDB與Chronograf。首先先來安裝這兩個元件。我們以Windows為使用環境。請先到官網的下載頁面進行下載。

下載的是zip格式檔案,只要將之解壓縮即可。我們先來看influxdb.zip,解開後會有5個exe執行檔。主要的有兩個:influxd.exe是InfluxDB server,執行起來後可以看到這樣的畫面。server預設會使用port 8086來接收API指令。因此系統的安全設定上要允許使用這個port。資料庫預設的username及password都是root。

influx.exe 是client程式,我們可以利用這個程式來操作資料庫。直接執行後便可以連接到本機上資料庫。我們可以先試一個指令 "show databases" 來看看資料庫是否正常運作。如果有回應目前資料庫中databases清單,便表示系統正常運作。 (由於才剛剛安裝,應該只有 _internal 這個內部資料庫)
我們可以用exit指令來離開client程式。
InfluxDB使用的query language是InfluxQL,官網上有一頁做了與SQL的比較,大家可以參考一下。

接下來解開chronograf.zip後,有兩個exe。chronograf.exe是web server程式。執行後以瀏覽器連接 http://localhost:8888,就可以進入到視覺化界面。

我們可以先到configuration頁面檢視目前與InfluxDB server的連線狀況

到目前InfluxDB與Chronograf都完成安裝並可以正常執行。要停止這兩個server的執行,只要按ctrl-C即可。

下一篇,我們將寫個Python程式來為資料進資料庫,並在Chronograf中設定dashboard的圖表。

2019/01/14

時間序列資料與資料庫

再資料處理領域中,有一類型的資料是跟時間有密切關係的,這類的資料紀錄了某事物隨著時間變動的狀態,例如某商品的價格資訊、某廠房的環境溫溼度、某物流車的位置與速度等等。這些資料都屬於時間序列資料(Time-Series Data)。

這類型的資料有一些特質:

  • 資料是用時間排序的一組隨機變量,例如下圖,數值會隨著時間的變化而改變,因此時間是很重要的資料維度之一

  • 資料的產生可能是快速、大量且持續
    例如某個機台,每秒鐘會產生100筆資料,每筆資料約250KB,一天下來單單一個機台的資料量就高達2TB了,而且只要生產線保持運作,資料就不斷的產生
有相當多領域的資料都具有時間序列的特性,如計量經濟學、數學金融、天氣預報、地震預測、通信工程等等。尤其IoT物聯網應用蓬勃發展,時間序列資料的相關研究更會受到重視。

資料若要利用資訊系統來處理,儲存便是一個重要的課題。目前許多的應用中,會使用關聯式資料庫加上一個time-stamp欄位來解決這問題。若檢視時間序列資料的特質,我們要考量的因素有:

  • 資料是源源不斷產生,而且鮮少需要修改,因此insert的效能會是重要因素,update反而不那麼重要
  • 在一些應用中,資料的產生是快速且大量的,因此擴展性(scalability)就相當重要
  • 時間的運算常會是取回資料時常用到的功能,所以時間運算的效能很重要
對關聯式或其他資料庫來說,對上述的幾點的表現並沒有那麼亮眼,因此,有一時間序列資料庫(TSDB; Time-Series Database)這個分支,針對了時間序列資料的特質來量身打造了。目前市場上有相當多的TSDB系統,常見的可參考這篇wiki資料。目前較熱門的就以InfluxDB與Kdb+為主了。這一篇比較表也很有參考價值。

下一篇,我們將開始安裝與使用InfluxDB。

2018/10/17

Spring Boot 試作紀錄

工作上的系統,架構常用到micro-service,建立這些服務,常常都需要自己架構出需要的服務元件。常讀到Spring Boot的介紹,對它的自動化很好奇,趁著有個小空檔來試作一下。

試作目標:
  • 支援 static resources
  • 支援 servlets (HTTP request handler),收到request後,可根據client的資訊作回應
  • 嵌入Tomcat或Jetty

使用環境:Eclipse 2018-09

安裝 Spring Tools
進入Eclipse後,點選 Help | Eclipse Marketplace...
以 "spring tool" 就可找到 Spring Tools 4,點選Install即可安裝

建立 project
我們利用剛剛安裝的Spring Tools提供的工具來建立Spring Boot專案。
首先在主選單點選 File | New | Other...

選擇 Spring Boot | Spring Starter Project,然後進入Next

接下來要設定專案的相關資訊了,雖然事後都還可以調整,不過一開始就先設定好,後面可以省些事

幾個比較重要的欄位:
  • Name (專案名稱),這預設會連動到下面的Artifact ID
  • Type, Java Version, Language都使用預設,Packaging有Jar與War,看未來想打包成何種形式,可以在pom.xml中改變
  • Group (Group ID) 用來識別此專案的名稱 (可參考這裡),可以在pom.xml中改變
  • Artifact (Artiface ID)在未來產生Jar file時會用到這名稱,可以在pom.xml中改變
  • Version用來記錄版本,在未來產生Jar file時會用到,可以在pom.xml中改變
  • Description用來對這個專案作一個描述,可以在pom.xml中改變
  • Package則是建立程式時預設的package架構

接下來就要選擇專案需要的dependent libraries了,例如若專案需要用到PostgreSQL的JDBC driver,就可以直接勾選,Maven就會自動幫你下載了。
目前試作的專案只需要Web,所以就勾選Web即可。

點選Finish,Spring Tool就開始幫我們產生專案了。

Spring Tool幫我們產生了一個程式 GreetingApplication.java。這程式的內容很簡單,就只是啟動Spring Boot的應用


建立靜態網頁
靜態網頁與相關資源(js, css, etc.) 可以放在src/main/resources/static下。在此專案我們就放一個簡單的網頁。


page.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Welcome</title>
<link href="css/page.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h1>Hello, welcome to static page.</h1>
</body>
</html>


page.css
h1 {
color: #ff7777;
font-style: italic;
}


建立Servlet (request handler)
接下來我們要進行動態回應的部分。我們列出需求:

  • 當使用者以POST或GET連結到 http://:8080/func時,則會由此handler來處理
  • 如果request parameter有name,我們就回覆 Hello, !,如果沒有name,則回覆 Hello, world!
  • 列出使用者的IP

我們建立的handler如下 (RequestHandler.java)
@RestController
public class RequestHandler {

    @RequestMapping(value="/func", method={RequestMethod.GET, RequestMethod.POST})
    public String handler1(@RequestParam(value="name", required=false) String name,
            HttpServletRequest request, HttpServletResponse response) {
        String user = (name==null || name.isEmpty())? "world": name;
        String host = request.getRemoteHost();
        String result = String.format("Hello, %s! You are from %s", user, host);
        return result;
    }
}


對此程式片段作一個註記:
  • @RestController:Spring採用MVC架構,註冊此程式為Controller
  • @RequestMapping:註冊此方法負責特定request的處理。value的值為URL對應的path,method則標記了要處理的方法
  • @RequestParam(value="name", required=false) String name
    Spring Boot會幫我們解析parameters,你可以將你需要的parameters都列出來。
    required預設為true,如果為false則表示此參數可能不存在於query string中
    解析出來的name值會放到字串name中
  • 因為我們需要使用者的IP,這需要從request物件中來取得,因此我們handler1的參數列加上了HttpServletRequest request,以便在程式中抓到IP
  • 雖然不會用到,我們也加上了HttpServletResponse response,若程式需要對response object作一些設定可以使用。在這個專案中是可以拿掉


執行
如果熟Eclipse的朋友就知道有很多種執行方法。這裡介紹其中一種,在project上按右鍵,Run As | Spring Boot App 就可以執行了


透過瀏覽器來看靜態網頁
以及動態內容


產生 JAR file
在project上按右鍵,Run As | Maven install就可以產生jar file了。

產生的jar file會放在專案的target目錄下。

所產生的jar file包含了專案所有需要的資源與libraries,因此只要把這個jar file放置到要執行程式的機器上,就可以執行這個服務了!
java -jar greeting-0.0.1.jar


由於這個jar file包含了必要的libraries,因此其file size大概都從15MB起跳。對大部分的運行環境應該都還能接受。

改內嵌 Jetty
Spring Boot 在 web應用上是預設內嵌Tomcat,若要改為使用Jetty,則需要修改pom.xml。現有的pom之片段是:

在這裡將Tomcat排除掉,再加入Jetty

這樣就可改用Jetty為平台了

修改port
這個系統預設是使用port 8080,若需要修改,可以直接在src/main/resources/application.properties中加上下列這行 (例如要改為port 88)
server.port = 88

2018/04/16

主管守則

擔任主管,就沒有抱怨的權力!
如果你抱怨的是部屬,那就顯露了你缺少了識人育人用人的能力
如果你抱怨的是同儕,那就顯露了你沒能經營人脈、圓滿處事的能力
如果你抱怨的是上司,那就顯露了你沒有向上管理、讓長官安心放心的能力
如果你抱怨的是客戶,那就顯露出你沒能儘早看出客戶要的是什麼、在意的是什麼
如果你抱怨的是供應商,那就顯露出你拿了棍子與蘿蔔還管不好那頭驢子
如果你抱怨的是大環境,那就顯露出你沒有洞察機先的遠見

問題來了,作為一位主管,別再只是抱怨了!
因為解決問題就是主管的天職
在動嘴巴前,請先動動腦子展現出你比部屬優越的擘劃能力
在動手指前,請先用用關係把部屬拿不到的資源協調出來
在下決策前,也請聽聽部屬們的聲音,並適時地採納
然後捲起袖子,帶著夥伴們一起解決問題
如果問題沒能解決,就請拿出你的肩膀
如果問題順利解決,就退後一步,讓你的夥伴們站在你的前面

因為,你是主管!

2017/11/09

Python群集型態整理

常用的Python群集型態有list, tuple, set, dict,把它們的特性整理一下

ListTupleSetDict
element typeobjectobjectobjectkey-value
(object-object)
orderedYYNN
mutableYNYY
duplicableYYNN
(unique key)
example[1, 'b', 3, 3](1, 'b', 3, 3){1, 'b', 3}{'a': 1, 'b': 'hello', 'c': 'hello'}

2017/11/02

TensorFlow的初體驗

TensorFlow在Linux下的問題似乎比較少,所以用VMware Workstation Player裝了一個Ubuntu 16.04_64,用一個乾淨單純的環境來學習TensorFlow

裝好Ubuntu後,接著要安裝Python、pip與TensorFlow。我是follow TensorFlow 基礎篇〈上〉這篇文章來進行安裝。要稍稍注意目前TensorFlow的版本是搭配Python 3.5.x,在3.6.x或以上可能會有問題(也許未來TensorFlow會釋出新的版本來支援3.6.x以上的版本)。

由於我所安裝的Ubuntu已經內含3.5.2,所以不需要再安裝Python 3 (可先執行python3即可知道現有的版本)。如果你的系統沒有Python 3,為確保安裝的是3.5.x,建議安裝的指令改用:
sudo apt-get install python3=3.5.*

執行step 6時,也許你會在執行tf.Session()時遇到印出一堆訊息,由於這個message的severity是I,代表只是information,所以可以不去理會。


安裝完成後,可以閱讀這一篇TensorFlow 基礎篇〈中〉來了解一下TensorFlow的資料型態與基本操作。逐步的玩過每個指令,熟悉一下TensorFlow的操作邏輯。

接下來就要進入機器學習的主菜了。還是跟隨著TensorFlow 基礎篇〈下〉這篇為學習的主軸。這一篇利用MNIST資料庫來進行手寫數字辨識。
不過這篇有一些說明不夠清楚,或是操作上會遇到問題之處,可以參考一下其他文章:

  • 如果想更清楚瞭解one-hot encoding/vector,可以看這裡
  • 對於MNIST 資料集,可以先看這篇說明,照這篇的介紹來學習應該可以更了解。不過這一篇會用到matplotlib,而這個又依賴了Tkinter,所以要先用下面兩個指令來安裝matplotlib與Tkinter:
    sudo python3 -mpip install matplotlib
    sudo apt-get install python3-tk
  • 接下來我透過這兩篇Softmax Regression 模型解說實作 MNIST Softmax 模型來了解Softmax
  • 很重要的是,官方文章絕對是最好的參考資源:TensorFlow官方教學
  • 這位前輩分享的學習筆記絕對要致敬一下!

2016/07/27

iBatis遇到Postgresql的JSON/JSONB type

Postgresql等資料庫在較新的版本中,都提供了JSON/JSONB這樣的data type,這提供了開發者更動態的便利性。關於JSON/JSONB可以參考這裡,本文就不贅述。

如果是使用iBatis來做ORM,又該如何與JSON做mapping呢
這裡記錄一下我的試做

我的table結構是
CREATE TABLE public.test
(
  oid bigint NOT NULL,
  data text,
  jdata1 json,
  jdata2 jsonb,
  CONSTRAINT "PK_TEST" PRIMARY KEY (oid)
)


Java class是
public class CTest {
private long oid;
private String data;
private String jdata1;
private String jdata2;
  // getter/setter略
}

則我們可以用下列的iBatis mapper XML
<resultMap id="test" type="CTest">
<result property="oid" column="oid"/>
<result property="data" column="data"/>
<result property="jdata1" column="jdata1"/>
<result property="jdata2" column="jdata2"/>
</resultMap>
<insert id="newTest" parameterType="Map" statementType="PREPARED">
insert into test (oid, data, jdata1, jdata2)
values (#{oid}, #{data}, cast(#{jdata1} as json), cast(#{jdata2} as jsonb));
</insert>
<select id="getTest" resultMap="test" parameterType="Map" statementType="PREPARED">
select * from test where oid=#{oid};
</select>


這樣就可以用下列的iBatis mapper程式來存取table了
public void newTest() throws Exception {
JSONObject o1 = new JSONObject();
o1.put("name", "Yoyo Chen");
o1.put("age", 25);
o1.put("qualified", true);
Map map = new HashMap();
map.put("oid", 333);
map.put("data", o1.toJSONString());
map.put("jdata1", o1.toJSONString());
map.put("jdata2", o1.toJSONString());
execute("newTest", map);
}

public CTest getTest(long oid) {
Map map = new HashMap();
map.put("oid", oid);
CTest t = (CTest) selectOne("getTest", map);
return t;
}


ps. execute()與selectOne()是我的methods,主要是包裝了iBatis的methods

2016/06/07

Socket Options

開發網路程式的朋友都應該用過socket,而socket中除了基本的方法與屬性外,還有不少options。如果觀察Socket的JavaDoc,你一定會發現有很多關於SO_XXXX、IP_XXXX、TCP_XXXX之類的屬性。這些屬性多半與通訊的連線成功或傳輸關係不是那麼大。但是,如果程式需要關注到效能,那這些屬性就會扮演重要的角色。

從Java的角度來檢視一下這些options:


IP_TOS


TOS(Type-Of-Service)的值必須介於[0..255]間。RFC 1394中則定義了下列

  • 0x00 -- normal service
  • 0x02 -- minimize monetary cost
  • 0x04 -- maximize reliability
  • 0x08 -- maximize thrughput
  • 0x10 -- minimize delay

程式可依需要將這些值做bitwise運算,來設定通訊時的等級(例如0x14是指minimize delay and maximize reliability)。
不過並不是所有的設備都會依照這個option的設定來進行傳輸,但是有需要還是可以設上。

SO_LINGER

linger是拖延的意思,這會影響Socket.close()的速度。
如果觀察TCP State Diagram,當我們做close socket的動作時,連線兩端還是會做許多複雜的動作。其中active close這端在TIME_WAIT會等待 2*MSL (Maximum Segment Lifetime)的時間,來確保送出的資料可以被另一端接收到。在RFC 793中提到了MSL是120秒。不同的OS與應用會去調整這個值,目前多數的Linux、BSD及Windows是30秒。

對於寫server-side程式的人來說,linger時間太長,你的程式雖然已經close這個socket了,但是在底層並沒有真的關掉,還在TIME_WAIT的狀態,造成了程式在短時間內沒有辦法再bind到這個port。
現在的internet速度與穩定性已經比早年好太多了,應該不需要這麼長的linger time了。如果你的程式需要較多的socket開關操作時,可以考慮把linger time設小一點。

如果把linger time設為0呢?則程式會送出設有RST flag的封包給對方,可以達到快速結束連線的效果。但是,對方的程式可能會發生 "Connection reset by peer" 的錯誤。

SO_TIMEOUT

這個時間是Socket.getInputStream().read()會被block的等待時間長度。如果read等待超過這個時間長度,則會丟出java.net.SocketTimeoutException。
SO_TIMEOUT的值須大於0,若為0,則視為無止盡。
這個值設太小,就容易常發生timeout的現象;若設太大,有時程式又會被block太久,反應速度變長。所以如何拿捏就要看應用類型而定了。

SO_SNDBUF / SO_RCVBUF

對底層的傳送/接收緩衝區大小的建議值。
這只是建議值,底層會依照所設定的值,在參照實際系統狀況來決定這兩個緩衝區的大小。
前輩建議,如果是在通訊較不好的環境,或是瞬間傳送的速度很快,那SO_SNDBUF可以設大些,這樣可以確保TCP sliding window夠大,足以容納一值無法成功送出的資料。
如果是電腦的處理速度慢,那SO_RCVBUF就要設大些,免得來不及處理,資料掉了。

SO_KEEPALIVE

如果socket建立了,但是長時間(2小時,視OS實作而定)沒有進行資料傳送,這個連線很可能會被中間的某個網路設備中止。為了避免這情形發生,如果啟用了SO_KEEPALIVE,則socket會自動發出一個keepalive probe給對方,對方也會回應回來,讓網路設備知道這個連線仍在使用中。
對有些應用或環境來說,2小時真的太長了(尤其是行動網路,有些系統商大概不會讓你站用連線這麼久),而且對Java也無法變更改時間值,所以許多應用就會自己實作ping/pong的機制來當作keep-alive。

SO_OOBINLINE

當連線有許多資料排隊要送時,如果有一緊急事件要插隊通知對方,就可以用 OOB (Out-Of-Band) Inline。啟用SO_OOBINLINE,並可透過Socket.sendUrgentData來即時送出 1-byte的資料給對方。
不過,直到Java 8,都沒有看到可以接收urgent data的方法。所以...

TCP_NODELAY

在提這項之前,要先瞭解Nagle's algorithm。簡單的說,想想一家貨運公司,如果一收到小貨物就馬上出車送貨,對公司來說整個營運效能就不高,對整個道路來說就會增加車次,容易造成路上塞車。
若是貨運公司將這些收到的小貨物累積到一定的量才出車送貨,那公司的營運效能才能提高,不會造成浪費。但是這就會造成最前面的幾件貨物要等很久才會被送出。
TCP_NODELAY就是停用Nagle's algorithm,來提高throughput。
如果所開發的系統常會傳送小資料,這個選項就該納入考慮。


以上做為工作上的紀錄,方便未來查閱。也希望對大家都有些幫助。

2016/05/05

JavaMail透過GMail送信遇到的問題

這樣的技術文件,網路上應該很多了,那些詳細的實作就不贅述
套著網路上的資訊,使用了下面的參數


結果遇到了下列的exception

javax.mail.MessagingException: Can't send command to SMTP host;
  nested exception is:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.mail.smtp.SMTPTransport.sendCommand(SMTPTransport.java:1564)
at com.sun.mail.smtp.SMTPTransport.sendCommand(SMTPTransport.java:1551)
at com.sun.mail.smtp.SMTPTransport.ehlo(SMTPTransport.java:935)
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:426)
at javax.mail.Service.connect(Service.java:310)
at javax.mail.Service.connect(Service.java:169)
at javax.mail.Service.connect(Service.java:118)
at com.ruby.vtun.entity.GeneralMail.send(GeneralMail.java:133)
at com.ruby.vtun.entity.GeneralMail.main(GeneralMail.java:161)
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1884)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:276)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:270)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1439)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:209)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:878)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:814)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1016)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1312)
at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:702)
at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:122)
at com.sun.mail.util.TraceOutputStream.write(TraceOutputStream.java:114)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
at com.sun.mail.smtp.SMTPTransport.sendCommand(SMTPTransport.java:1562)
... 8 more
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
at sun.security.validator.Validator.validate(Validator.java:260)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:326)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:231)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:126)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1421)
... 19 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:196)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:268)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380)
... 25 more


經過被蹂躪了一陣子,總算發現是我的防毒軟體Avast作怪!把Avast關掉,就可以正常送信了。
要解決這問題,除了關掉Avast外,可以透過更改Avast的設定來解決,因為目前是使用port 587來送,只要把587從Avast的設定中拿掉,就可以正常送信了

2016/02/03

GCM SERVICE_NOT_AVAILABLE

今天又遇到怪事了
在修改一支Android APP,這支APP有用到GCM來做push notification,但是修改的點只有在UI的部份
修改完放到Samsung Galaxy Mega(4.2.2)一跑就當,以前沒發生過這問題
查了一下,是跑到GoogleCloudMessaging的register()就當,這段程式應該是很標準的寫法

if (gcm == null) {
    gcm = GoogleCloudMessaging.getInstance(parent);
}
String regId = gcm.register(Global.GCM_SENDER_ID);


丟出來的exception是SERVICE_NOT_AVAILABLE
java.io.IOException: SERVICE_NOT_AVAILABLE
at com.google.android.gms.gcm.GoogleCloudMessaging.register(Unknown Source)
at com.ruby.switch2go.manager.GCMHelper$GCMAsyncTask.doInBackground(GCMHelper.java:76)
at android.os.AsyncTask$2.call(AsyncTask.java:287)
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:856)

把程式放到ASUS Nexus 7(6.0.1)上跑,耶!一樣的程式,卻可以正常執行喔。所以應該不是程式的問題,那...難道是Android版本的問題?

再找來另一支6.0的手機,嗶~~~,還是SERVICE_NOT_AVAILABLE!
看來應該不是Android版本問題了
這幾台的Google Play Service都是8.4.89版的

再回到Samsung GALAXY Mega,用另一個類似但sender ID不同的程式來跑,卻又正常了

看了這篇Google Cloud Messaging is Extremely Unreliable for Push Notifications,手機端能檢查的都看過,好像也無解。

那~~~問題在那裡?真的是Google server-side的問題嗎?
是不是不同的phone token與sender ID會分派到不同的server去處理,而負責DUT的server剛好這時候有問題了?

果然,時間是最好的解藥。經過兩個多小時的等待,一切都恢復正常了。看來,是GCM server-side有問題了。




2015/09/25

Can't receive GCM notifications in some environments

This is a strange issue about Google push notification.

I am trying to send notifications to my APP. In office, my Samsung Galaxy Mega 5.8 (4.2.2) and Nexus 7(5.1.1) can receive the notifications with wifi connection, but the Infocus M210 (4.4.2) fail to get the notification.

To narrow the problem, I try Push Notification Tester to do the test on M210 and Mega 5.8. The M210 also misses the notifications.

I take these 2 phones home. The M210 receives all the missing notifications when it connect to the wifi in my home. I try Push Notification Tester again and both phones works fine.

All phones work fine but the M210 in my office's wifi. The M210 can work fine in home's wifi only. I can't figure out why M210 can't get the notifications in my office. Is the wifi the root cause? Surfing on the Internet, I still can't find the reason and solution yet.

2015/06/05

Blank window issue on Safari Mobile and a quick solution

I am working on an embedded project. The device provides simple Web HTTP interface to interact with this device. It works fine on all browsers but Safari Mobile on iOS 8.3.

Surfing on Google, there are some problems on Safari Mobile (article 1, article 2, article 3, article 4, article 5). They sound like not same as what I am facing to. I monitor the packets captured by Wireshark, and found that Safari Mobile didn't load all resources(css, js, ttf, etc.) completely causes this problem. The Safari Mobile communicates with Web server in HTTP pipeline, while the others (Chrome, Firefox, Opera, IE, Safari Desktop, etc.) don't. (The others will reuse the connection, but won't pipeline them). That is the major difference!

In theory, the HTTP pipeline can speed up requests by reducing the number of round trip times. But there are some articles(articles 6, article 7, article 8) comment on this point. This is not what I want to discuss in this page. Let's back to my problem.

The HTTP pipeline raises lots of problem. Some developers submit tickets to Apple, but there is no plan to solve the problem. I try to find

I found an easy workaround for this issue at last. This problem can be solved by setting the connection field of the HTTP response header. I force the connection field to be "close" if the browser is Safari, and it works! In Java Servlet, you can set it as
response.setHeader("Connection", "close");

(or you can set the header field in Filter), or in PHP
header("Connection: close");

or you can set the field in Web server setting.

In Safari Mobile, it will close the connection when receive the response, and won't send further requests thru this connection. That is, no more HTTP pipeline from Safari Mobile. I am not sure is this the best solution or not, but it really solve my problem.

2015/05/19

你如何設計你的產品?

創業者請想清楚,該關注自己的產品還是需要解決的問題?
「每次他們發現新問題或新需求時,就不停地添加新功能。最終結果就是產品變得異常複雜,功能零亂,又沒法徹底解決用戶需求」

這篇文章讓我想起昨天在辦公室裡的事
昨天下午三點多接獲一個新指令,要做一個新功能。
思考了一下,把疑點跟主管討論了一下
嗯...這功能A在操作上會自己與自己抵觸
OK,主管決定修正為功能B
再思考了一下,覺得還是怪怪的
再去找主管,仔細的問這功能是要給誰(actor)用?這功能要呈現什麼價值(use case)?
被我這樣一問,大家才搞清楚誰是使用者,操作情境是如何,也才發覺原來功能A與功能B都不是最好的解法。從整個scenario重新構思,分析出我們的需求應該是功能C

在設計產品時,得不斷提醒自己,這是要給誰用?你的產品在那個情境中,能提供他們什麼價值?

2015/05/04

iOS 7/8 Safari空白畫面

如果你的網站在Desktop版的Safari是正常,可是在iOS 7/8上的Safari卻會出現空白畫面,一直loading不停,那你可能遇到Safari的bug了。可參見: http://tech.vg.no/2013/10/02/ios7-bug-shows-white-page-when-getting-304-not-modified-from-server/ 

這問題來自當要用Safari連上一個網址,這個網址你之前曾要連,但是因為網路、使用者中斷或是其他問題,造成某些resources(html, javascript, css, 圖檔等等)並未完成下載,但是Safari卻當成已完成下載。當使用者要重新連這網址時,在reload時會問server從上個時間點後有沒有改變過(雖然上次沒有完成下載,但是Safari卻當成已下載過),而server此時可能回覆304 code表示沒有改變過。因此Safari就會從cache來取得這個resources,但是在cache裡的resources本來就不完整,造成Safari在呈現或呼叫webkit javascript engine執行時便發生錯誤,出現空白畫面或一直在下載中。 

對此問題有興趣,可以用"ios safari 304"為keywords在google上搜尋,可以找到不少討論此議題的文章。 

目前Apple似乎還未將這問題列為bug,所以可能還沒有修復的時間表。至於網路上有些人有提出些workarounds,主要都是將server-side改為不回應304,或是針對Safari不回應304,讓瀏覽器對所有的resources都一定重新取得,而不是使用cache裡的。不過這對使用iOS 7/8的iPhone/iPad來說,將會耗掉更多的電力與頻寬(如果不是吃到飽就要小心了),但對網站經營者來說,為了達到Apple常掛在嘴上的「使用者體驗」,似乎也沒什麼其他的解法了。

2015/04/28

在HTML element要處理left-click與right-click

被指派一個任務,頁面上的一個元件,當right-click與left-click時,要有不同的反應,IE 8也要能兼容,且必須在mouse up時才觸發。
如果是mouse down就比較好處理,若mouse up,這個mouse event會bubble up,無法用stopPropagation()來停止,所以就不能直接bind在mouseup上了

最後用了幾個方法組合起來,來達成這個需求,HTML的tag如下,bind onclick來處理left-click;bind oncontextmenu來處理right-click:
<a href="#" onclick="return handler(event)" oncontextmenu="return handler(event)">CLICK</a>
JavaScript的function則如下
  function handler(e, id) {
    var clickType = 1; //1:left-click; 3:right-click
    if (e.which) {
      clickType = e.which;
    } else if (e.type) {  // for IE 8
      if (e.type=="click") {
        clickType = 1;
      } else if (e.type=="contextmenu") {
        clickType = 3;
      } else {
        clickType = e.button+10;
      }
    }
    alert("click type: "+clickType);
    return false;
  }
在Chrome 42, FireFox 37, Opera 28, IE 11/8上測試都OK

2015/01/08

將會影響 IoT 走向的組織

IoT是個熱門話題。不過各家有各家的想法與作法。未來會怎麼走?
也許,就要看下列的這些組織會怎麼走,以及那些組織的運作比較有力了

AllSeen Alliance
成立於2013,發起人有Qualcomm, Cisco, Panasonic及其他家電業者。目前約有100個會員。這個聯盟主要是希望能給家庭與商務等使用不同的OS與network protocols的設備一個方式來互相發現對方並進行溝通。Qualcomm貢獻了一個open-source software framework: AllJoyn 給這個聯盟。目前已經有支援AllJoyn的設備上市了,Microsoft的Windows 10也已內建AllJoyn於其中。

Open Interconnect Consortium(OIC)
成立於2014七月,會員包含了Intel, Cisco, Samsung, Dell, HP, Lenovo, GE, MediaTek, Atmel,Siemens等。會員數目前約50,規劃了許多計劃書以協助設備間互相發現與溝通合作。預計在2014年底釋出程式碼給開發者,正式規格將於2015上半年提出。

Thread Group
由ARM, Samsung, Google Nest Labs等於2014七月發起,所推動的是家庭低功率設備所組成的mesh網路。目前會員數約50。每個device都賦予一個IPv6位址。目前市面上已經有支援Thread Group提出之protocol的晶片。此group目前僅定義device netwokr這層,因此未來不排除在較高層套用AllSeen或OIC之架構。預計在2015上半年開始對產品進行認證。

Industrial Internet Consortium(IIC)
由GE, Cisco, IBM, Intel, AT&T在2014五月組成的協會,主要是鎖定在Enterprise IoT。目前會員超過百家,包含了Microsoft, Samsung, Huawei等。協會將不會制定標準,會將工作重心放在定義標準需求、設計參考架構、建立testbeds等。

IEEE P2413
2014七月召開了第一次會議,總共有23家廠商和組織與會。預計在2016年完成標準的制定。

2014/12/25

Android Studio 筆記

最近Google releases正式版的Android Studio,並宣稱此工具將成為official IDE for Android。那Android未來還會繼續維護與發展ADT嗎?從Android用official IDE這字眼,並在官網上鼓勵大家轉到Android Studio的態度,個人覺得以Eclipse為基礎的ADT可能會走向尾聲了(參考這一篇)。

因為Android Studio的專案結構與觀念與ADT有蠻大的差別,所以一定要來瞭解一下

讀了一些網頁,把一些重要的記錄下來

Android Studio Overview
官方的資料一定是不可或缺的
我覺得這裡的workflow章節很重要,可讓我們可以瞭解一下這工具的使用概念
這裡稍作紀錄一下,Android Studio開啟是以專案(project)為單位(Eclipse則是workspace)
每個project可以包含一至多個modules(至少要有一個application module)。Module是個容器,用來存放與組織所需要的source codes與resources。Module又分為四種:
  • Application Module:可用來產出.apk檔來安裝至Android設備
  • Library Module:如其名稱,用來存放可被其他projects或modules共用的source codes與resources,以達到reuse之目的
  • Test Module:用來包含可對application projects進行測試的codes。其中講到"are built into test applications that run on a device",這等有時間可以來多瞭解一下
  • App Engine Module:可以與Google App Engine(GAE)結合,來整合device與雲端成為一體。這一塊對應該是做IoT的朋友可以多留意的

Android Studio vs. Eclipse: What You Need To Know
如果已經用慣Eclipse的朋友,可以先看看這篇,瞭解這兩者的異同
另外這一篇也可以參考一下

Android Tutorial for Beginners: Part 1
Android Tutorial for Beginners: Part 2
Android Tutorial for Beginners: Part 3
就如標題,這是針對正式版(v.1.0)所修改過的入門文,不是用之前beta版,有正確的畫面截圖,在學習時應該會減少很多混淆與誤解

Gardle
Android Studio並未採用Ant做為其build tool,而是選用了Gardle。因此若想把Android Studio玩好,把Gardle讀通也是必要的。
也可以參考Android官網上的Gradle Plugin User Guide