Showing posts with label groovy. Show all posts
Showing posts with label groovy. Show all posts

2010/11/13

應用開發的簡化

容易的方式

雖然常把實用新穎的技術簡化, 想辦法放入既有系統中, 讓夥伴、客戶都能上手使用, 但現實中卻還是不夠。

前幾天幫客戶除錯, 看問題時, 才知道他們做了什麼 … 有點類似 Web Services (偽), 卻是用 PHP。他們在生產過程中, 要把某個事件記錄在另一個資料庫。當然用 PHP 沒什麼問題, 只是把所有執行相關工作都寫在一個檔案裡頭的做法, 很明顯是從書上抄下來的範例, 再改一改的成果。

對許多人來說, 書上有類似的範例, 能照本宣科就拿來用就好。這大概是某些語言長年佔據 排行榜 的原因, 但時至今日這股『只要能動』的風潮漸漸改變。在 Web 時代大部分拿 SQL 就直接用, 到了 Web Services 時代, 分工愈來愈細, 要求愈來愈多, 許多程式的運作都以物件為基礎。並非談物件的優劣, 而是評估現實中若使用物件比 SQL 方便, 比如資料的呈現(xml, json), 處理(鷹架), 以及檢核這些作業, 自然而然趨勢也跟著改變。

這裡還有一個關鍵點, 以上面出問題的 PHP 來說, 不管怎樣只要一個檔案就做完收工。原有系統在程式方面雖然也能簡單做到, 但要連到他們內部使用的資料庫(MySQL)這種配置就不太容易。因為原系統採 N 階層架構, 要多接個資料庫, 雖然可做到, 但得更動不少層。
或許從仿 PHP 單一檔案開發起個頭, 比較容易些。

簡單的實現

PHP 帶領 LAMP 的風潮, 在 約定優於配置 之前, 受到許多開發人員喜愛。著重四個方面: 簡單編寫, 簡單散佈, 本機開發, 便宜且到處可託管。除了最後一個外, 前三項直接讓開發的人獲得好處。預設與 MySQL 搭配(以 5.3.3 來說, 還有 PostgreSQL, Oracel, Sqlite), 因此只要資料庫能夠運作, 程式就能寫, 能動, 能測。程式執行後, 也能立即從資料庫看看結果是否正確。

再複雜些的系統, 除了語言本身, 還得準備更多暖身課程。

在今天之前, 也一直覺得 PHP 劃下簡單的境界, 是無法挑戰的。原本的 Groovy 知識, 只想做到很接近, 但查了查資料, 發現結果還不錯。

前提是有個 MySQL 在正常運作, 假設有資料庫 mydb 以及表格 mytbl 如下:
simp_mysql.png

有兩欄(name, tel), 以及兩列資料。

先確定裝妥 JDK 1.6 以上版本, 再到 Groovy 首頁 下載 1.7 之後版本, 然後執行 bin/ 目錄下的 groovyConsole 能直接寫程式。

拷貝、貼上程式碼:


@Grapes([
@Grab('mysql:mysql-connector-java:5.1.13'),
@GrabConfig(systemClassLoader=true)
])
import groovy.sql.Sql

def dbUrl="jdbc:mysql://localhost/mydb?useUnicode=yes&characterEncoding=UTF-8"
def driverClass="com.mysql.jdbc.Driver"
def sql=Sql.newInstance(dbUrl,"root","root",driverClass)
sql.eachRow("select * from mytbl") {
println it
}


注意, 資料庫位置 localhost, 名稱 mydb, 帳號/密碼 root/root, 還有表格名稱 mytbl 可能需要置換。然後選擇執行, 可用工具列右邊屬來第二個圖示, 或選單裡頭 Script/Run。第一次可能久一點, 因為缺少連接 db 的程式庫(不像 PHP 有附加), 要花些時間下載, 之後就正常。LAMP 要義的第一項簡單編寫, 第三項本機開發, 可順利達成。整個結果, 應該和下圖差不多:
simp_groovy.png

除了讀取資料庫, 其它新增、更新、刪除的 SQL 也都有, 請參考 Groovy SQL。確定沒問題, 就存檔保留。

不過這時候或許有人會問: PHP 在命令列(command line)下執行很快的, Groovy 那能比? 那請試試 GroovyServ, 雖然有點作弊, 但就像 IE 比其他瀏覽器快, Office 比其他工具快, 差不多就是那種的方式 …

換不同資料庫, 應該只要改上面截圖中第 2 列程式庫, 第 7 列連接資料庫網址, 第 8 列資料庫套件名稱, 可能再加上第 9 列的帳號、密號, 其它步驟類推即可。

以上示範, 算真正的單一檔案寫完收工, 也簡單到少有的地步。

那放到網站是否很複雜?

在 Java 使用 Tomcat 會比 LAMP 中 Apache 容易。相同方式 tomcat 5, 6, 7 版本可通用, 這裡以 tomcat 6.0.29 示範。下載後解開, 把長長的目錄改為 tomcat6/。

在 tomcat6/ 下的 webapps/ 裡頭建 groovy/ 目錄, 再建 WEB-INF/。

把 Groovy 1.7 的 lib/ 複製到 WEB-INF/ 裡頭。

在 WEB-INF/ 增加 web.xml 檔案, 內容如下:


<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
<display-name>Groovy Web Apps</display-name>

<servlet>
<servlet-name>Groovy</servlet-name>
<servlet-class>groovy.servlet.GroovyServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>Groovy</servlet-name>
<url-pattern>*.groovy</url-pattern>
</servlet-mapping>
</web-app>


而把上面讀取資料庫的 groovy 程式, 前幾列 @Grab 改一些 (有點尷尬, 之前寫法在 Web 不能用), 不過下列是通用的。(日後應該會針對此點修補 @Grab 才是)


ClassLoader.systemClassLoader.class.name = 'groovy.lang.GroovyClassLoader'
groovy.grape.Grape.grab(classLoader:ClassLoader.systemClassLoader
,group:'mysql', artifact:'mysql-connector-java', version:'5.1.13')
import groovy.sql.Sql

def dbUrl="jdbc:mysql://localhost/mydb?useUnicode=yes&characterEncoding=UTF-8"
def driverClass="com.mysql.jdbc.Driver"
def sql=Sql.newInstance(dbUrl,"root","root",driverClass)
sql.eachRow("select * from mytbl") {
println it
}


整個做法的結果應該和下圖相同:
simp_tomcat.png

以上方法做完, 執行 tomcat 6 … 一般用 bin/catalina.sh run 即可。

此時瀏覽器看到結果如下:
simp_web.png

單機寫完應用程式, 執行驗證完後, 隨即放上網站目錄去, LAMP 要義第二項的簡單散佈也可達陣。

簡單還要更簡單

原先想寫關於資料庫程式遷徙到 grails 的方式, 因為客戶的問題轉到另一個題目上, 與 PHP 的某種(通用?)做法比劃一下。

除此之外, 還有更積極的意義 … PHP 固然是寫資料庫和網頁程式的主流, 其相關資源可略窺一二。但 Java 連接的 IT 資源更廣泛, 像負責生產管理的 MES, 或財務處理的 AIS, 都由於加上 Groovy 的協助, 讓原本的系統有更大彈性, 也讓部份客製門檻降低, 例如一些報表、檢核的作業讓更多 IT 人員參與。可以想像一下 … 上面 SQL 資料讀入與 Web 輸出部份, 換成來自不同系統流程的資料, 以及 csv, xml, xls 或 pdf 輸出, 輔以適當的權限與稽核, 就可讓原本要獨立開發的程式變成簡單的腳本擋。

且如同 PHP 一般即改即用, 很簡單的滿足許多需求。

參考

2010/08/25

Avro 的使用與 Groovy Builder

Avro 是動態型別的資料序列化工具, 協助資料存放保留, 主要是讓網路化的應用程式有效率的溝通。如同使用語言設計模組溝通一般會透過界面方式, 在網路的服務常有著不同語言的實作, Avro 提供這些服務溝通的方式。

定義網路溝通的是通訊協定(Protocol), Avro 利用 JSON 定義通訊協定。

協定內包含有若干具名(name)的訊息(Protocol.Message), 每個訊息含有 3 種型態內容: 要求(Requst), 回覆(Response), 還有錯誤(Errors)。這些內容都以綱目(Schemas)定義內容的組成。

Avro 協助按綱目組成傳送的訊息。這裡有兩個關鍵: 一是組成, 一是傳送。

組成部份請參考 Patrick Hunt 先生的快速開始使用 Avro 範例 中可看出 python 與 ruby 都用語言本身的 collection 組成; 而 Java 則用 GenericData 套件。

傳送方面 Avro 有 HTTP 與 Socket 兩種通用方式, 服務端是 Server, 使用端是 Transceiver。如果用訊息服務(MOM)或搭配既有SOA,ESB系統, 就得抓訊息產生結果來操作。(在 Java 用 GenericRequestor, GenericResponder 中的 read/write 方法)

以 Groovy 與上面 Python 範例對連:
import org.apache.avro.Protocol
import org.apache.avro.util.Utf8
import org.apache.avro.ipc.HttpTransceiver
import org.apache.avro.generic.GenericData
import org.apache.avro.generic.GenericRequestor

def avpr = """
......
"""

def proto = Protocol.parse(avpr)

def trans = new HttpTransceiver(new URL('http://localhost:8080'))
def req = new GenericRequestor(proto, trans)
def message = new GenericData.Record(proto.getType('Message'))
message.put('to', new Utf8('wei'))
message.put('from', new Utf8('tcc'))
message.put('body', new Utf8('Hello 123'))
def params = new GenericData.Record(proto.messages.send.request);
params.put('message', message)
def ret = req.request('send', params)
println "Result: " + ret

其中 import 群下的 def avpr = """......""" 是把 src/avro/mail.avpr 定義的通訊協定拷貝進去。

如果不想用嵌入式, 可改為類似 python 範例中的:
def proto = Protocol.parse(new File('../avro/mail.avpr'))

再做 Groovy 的 Server:
import org.apache.avro.Protocol
import org.apache.avro.util.Utf8
import org.apache.avro.ipc.HttpServer
import org.apache.avro.generic.GenericData
import org.apache.avro.generic.GenericResponder

def avpr = """
......
"""

def proto = Protocol.parse(avpr)

class MyResponder extends GenericResponder {
public MyResponder(Protocol proto) {
super(proto)
}
public Object respond(Protocol.Message msg, Object req) {
return new Utf8("""Sent message ${req.message['to']}
from ${req.message['from']}
with body ${req.message['body']}""")
}
}
def res = new MyResponder(proto)

new HttpServer(res, 8080)

其中 respond(Protocol.Message msg, Object req) 的 req 為 GenericData.Record 在 Groovy 利用 GPath 直接操作相當便利。但客戶端用 GenericData 組成看起來頗辛苦, 想改用 Groovy Builder 式:
avro_builder.send { request {
message(to:'wei', from:'tcc', body:'Hello 123')
}}

寫個 AvroGroovyBuilder.java 放在 github。 (注意: 目前沒處理 List)

使用端就可簡化成為:
import org.apache.avro.Protocol
import org.apache.avro.ipc.HttpTransceiver
import org.apache.avro.generic.GenericRequestor

def avpr = """
......
"""

def proto = Protocol.parse(avpr)

def trans = new HttpTransceiver(new URL('http://localhost:8080'))
def req = new GenericRequestor(proto, trans)
def avro_builder = new org.tcchou.groovy.AvroGroovyBuilder(proto)
avro_builder.send { request {
message(to:'wei', from:'tcc', body:'Hello 123')
}}
def ret = req.request('send', avro_builder.params)
println "Result: " + ret

2010/05/06

Closure under Groovy


// using closure under groovy
// to display all contains in directory
def getFiles = { dir, rtn ->
dir.listFiles().each { file ->
if (file.isDirectory()) {
getFiles(file, rtn)
} else {
rtn.call(file) // action
}
}
}


getFiles(new File('/Users/tcchou/Desktop/ewtek'), { file ->
println file.getAbsolutePath()
})

2009/05/13

近未來 IT 程式開發

今年初(2009)看到 InfoQ 上匯集 2008 年度文章, 其中一篇 JRuby 開發者之一 Ola Bini 先生的相關文章 "Programming languages in future systems" , 將應用系統用到的語言類型分為三層, 印象很深刻 ... 因為幾乎花整個 2008 年的時間打造這樣的系統 ... 在讀到文章之前, 做起來不盡相同。在案子完成之際, 借用相似標題記錄一下這個過程。

大多數 IT 相關系統, 都是處理資料 ... 絕大部分的人都會同意這是領域(domain)主導, 不是技術主導。在一些廠商強力運作下, 開發人員近乎被催眠在執行一些固定工作 ... 選用主流技術(沒人喜歡意外), 然後踏上規劃, 分析, 設計, 實作, 測試, ... 的履帶。

在專案執行之前, 有 IT 主要廠商已下海幾年宣告失敗 ... 就不得不審視一些基本問題:
  1. 這是一個財務資訊系統 (AIS, Accouting Information System) 主要得完成 "成本系統" ... 屬資料市集 (Data Mart) 規模, 資料變異不大但資料量大。

  2. 使用者都全天候使用, 需求經常性改變 ... 使用角色的人數約開發者的 5 倍。

  3. 之前系統是 dbase 系, 因為原廠不支持, 決定換 Java。要接手的 IT 部門沒人有經驗 ...

審視第一點 ... 之前也習慣用 ORM, 但時薪較我日薪還高的廠商都搞不定的資料量 ... 立馬決定透過 SQL 把資料處理的問題都丟給資料庫, 捨棄物件導向方式。

關於第二點 ... 全天候使用, 帶給系統更大的壓力。而需求改變, 更是許多系統不穩定的致命一擊。但揉合這兩個問題, 使用腳本(Scripting)語言達成快速變更, 測試就交給使用者(與開發人員關係良好) ... 原則上是這樣, 還有一些方式, 下段再述。

而最末一點 ... 要從頭學習 Java, 並好好發揮不是每個人都辦得到。但主要是匯入/匯出、維護/查找資料、檢核、計算、報表、傳票 ... 這幾件事,大致就是 SQL 加上個別功能就收工。後來都是 SQL 測試完成後, 再用樣板(template)套入功能 ... 大約可完成八、九成工作。

以技術拆解來看是: SQL + 腳本語言 + 功能模組樣板 ... 有人認為 Java 可達成, 的確也是。但 Java 的問題在於, 直接放 SQL 勢必會分段(SQL 都是長得誇張), 再加上變數會更亂; 而做成獨立檔案(iBatis 做法), 或 Stored Procedure 或 View, 造成非功能性分割, 竊認為是修改應用程式不易的禍首。

使用腳本語言就一個功能一個檔, 很直覺, 清清處處 ... 開發過程曾有 SQL 部份算式要改, 還是使用者(只會 SQL 查資料)直接指出哪邊要改。

專案開始(2007年中), 就決定使用Groovy, 即便用過 jython, 當時也較成熟; 而 jruby 如日中天 ... 但偏好 Groovy 補 Java 不足的角度, 特別是支援類似 python 三引號(triple-quotes)的樣板字串(template string), 可直接寫 SQL 再加上需要的變數、函式都很容易 ... 除了 Groovy 本身支援的 SQL, 後來也嵌入EBNF 的語法描述, 再來做 DSL 處理也很方便。

回頭看打造的系統與 Ola Bini 先生宣告三層類型的對應:
  1. 穩定層(Stable layer): 沒與應用太多相關, 以靜態語言打造 ... Groovy 程式的載入、執行都是用 Java 寫的。

  2. 動態層(Dynamic layer): 與應用相關, 以動態語言打造 ... 功能模組樣板(匯入/匯出, 檢核, 計算, ...), 都是用 Groovy 寫的 ... 像報表小計方式百百種, 都一一解決。

  3. 領域層(Domain layer): 與應用相關, 以領域語言打造 ... 使用 SQL + Groovy 方式, 大抵 SQL 能動就了結。

其文中提到的 Drools 也有用到, 因為決策表格(Decision Table)對使用者更容易使用 ... 也在此做證言 :-)