2009年11月2日 星期一

spring-DM webcontainer 啟動

spring-DM 以 extender bundle 去偵測install進來的 bundle中
的META-INF/MANIFEST.MF含有:

Spring-Context: xxxxx/xxxx.xml

這個 header 的 bundle 並以這個 xml 為這個 bundle 建立一個屬於他的
ApplicationContext.....

與 war 整合時,則透過類似相同的概念,不過不是用同一個 extender 偵測,
而是要另外再加上 org.springframework.osgi.web.extender 這個 bundle,
他一樣會偵測 install 進來的 war (war 也可以被當做 bundle install),
並看 war 裡面的 META-INF/MANIFEST.MF 的 Spring-Context
但是不為 war 建立 ApplicationContext 而是將 war 丟給指定的 WebContainer,
預設為 tomcat,tomcat當然也是一個 bundle,再由WebContainer去建立ApplicationContext。

tomcat WebContainer 有自己原本的啟勳方式,但現在被當成一個 bundle,
就必須以OSGi的方式啟動,也就是 Activator ,而tomcat bundle本身沒有這一個Activator,
因此,除了tomcat bundle以外,還要再加裝一個Spring-DM提供的catalina.start.osgi-1.0.0.jar
由這個 start bundle 來啟動 tomcat。這裡的 tomcat bundle 和 tomcat start bundle
都可以在 Spring-DM 的 zip 檔裡的 lib 目錄下找到,目前試過只有 Spring-DM 提供的bundle,運作正常。

除了 tomcat WebContainer ,也可以使用 jetty 做為 WebContainer ,只要以 OSGi 的 fragment 機致,
去修改 org.springframework.osgi.web.extender 的設定即可,Spring-DM 文件中有寫:
http://static.springsource.org/osgi/docs/1.2.0/reference/html-single/#web:configuration:changing-deployer
但由於目前使用的 Felix Osgi Framework 未支援 fragment 機致,因此只能先以 tomcat 做為 WebContainer。

jetty 同樣也需要 start bundle,jetty fragment 、 start bundle 在 Spring-DM zip 檔的 lib 裡都可以找到。

2009年10月29日 星期四

ivy 新增 maven repository 以外的 repository

先在 build.xml 所在目錄下加入 ivysettings.xml 覆寫掉 ivy.jar 裡預設的 ivysettings.xml
僅修改 main-chain 部分內容如下:
<?xml version="1.0" encoding="utf-8"?>
<ivysettings>
<settings defaultResolver="default"/>
<include url="${ivy.default.settings.dir}/ivysettings-public.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-shared.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-local.xml"/>
<include url="ivysettings-main-chain.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-default-chain.xml"/>
</ivysettings>
${ivy.default.settings.dir}的位置就是 ivy.jar 裡的 org.apache.ivy.core.settings package
修改後改為指到 build.xml 所在目錄下的 ivysettings-main-chain.xml
新增 ivysettings-main-chain.xml 其內容如下:
<?xml version="1.0" encoding="utf-8"?>
<ivysettings>
<resolvers>
<url name="com.springsource.repository.bundles.release">
<ivy pattern="http://repository.springsource.com/ivy/bundles/release/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
<artifact pattern="http://repository.springsource.com/ivy/bundles/release/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
</url>
<url name="com.springsource.repository.bundles.external">
<ivy pattern="http://repository.springsource.com/ivy/bundles/external/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
<artifact pattern="http://repository.springsource.com/ivy/bundles/external/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
</url>
<chain name="main" dual="true">
<resolver ref="shared"/>
<resolver ref="public"/>
<resolver ref="com.springsource.repository.bundles.release"/>
<resolver ref="com.springsource.repository.bundles.external"/>
</chain>
</resolvers>
</ivysettings>

這裡新增的public和shared resolver 是原本ivy.jar就預設定義的,而新增的兩個 url resolver是 spring 的 SpringSource Enterprise Bundle Repository

安裝 ivy - 類似 maven 的 java 函式庫管理工具

下載 ivy 後將 ivy-x.x.x.jar 放到 ant 的 lib 目錄下…如:
/usr/share/ant/lib/ivy-2.1.0.jar
即可在 ant 的 build.xml 裡使用 ivy 的 tag
並且編寫 ivy.xml 定義所用到的函式庫,放在 build.xml 檔案的同目錄下。

2009年10月27日 星期二

META-INF/MANIFEST.MF 的最後一行注意事項

要多加一行空白行(多按一次Enter),否則最後一行的參數將無法被 jar 讀到。
如:
========= MANIFEST.MF 開始 ==========
Manifest-Version: 1.0
Bundle-Name: Service listener example
Bundle-Description: A bundle that displays messages at startup and when service events occur
Bundle-Vendor: Apache Felix
Bundle-Version: 1.0.0
Bundle-Activator: tw.bennu.tutorial.example.Activator
Import-Package: org.osgi.framework (<===最後一行參數)

========= MANIFEST.MF 結束 ==========

2009年10月23日 星期五

以 mutt 結合 msmtp 透過遠端 MTA 幫忙發信

mutt 是 linux 下常見預設安裝好的文字模式 mail client
但是,本身預設不但不包含 pop3、smtp 功能,甚至連郵件編輯器也是委拖其他軟體
同樣的我們可以安裝 msmtp ,並讓 mutt 委拖 smtp 功能給 msmtp 就可以寄信了。
安裝 msmtp
sudo aptitude install msmtp

在家目錄下建立 ~/.msmtprc 內容:
# Example for a user configuration file
# Set default values for all following accounts.
defaults
tls on #Gmail TLS有打開
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile ~/.msmtp.log

# A freemail service
account gmail
host smtp.gmail.com
from yourid@gmail.com
auth on #Gmail SMTP要認證
user yourid #gmail 帳號
password secret #gmail 密碼,明碼,記得chmod 600本檔案
account default : gmail

在家目錄下建立 ~/.muttrc 內容
set sendmail="/usr/bin/msmtp"
set from=yourid@yourdomain.com #gmail SMTP會覆寫

2009年10月22日 星期四

ssh disable 掉 password 登入

注意!進行此動作前需先確定可以透過 ssh-key 登入,
否則…主機最好別放太遠.....

編輯 /etc/ssh/ssh_config 檔
sudo vim /etc/ssh/ssh_config
設定以下三行為 no
ChallengeResponseAuthentication no
PasswordAuthentication no
UsePAM no
重新 reload ssh 服務
sudo /etc/init.d/ssh reload

測試,先將私錀改名
mv ~/.ssh/id_rsa ~/.ssh/id_rsa.backup
以 ssh 登入如果出現以下訊息表示ok (不再要求輸入密碼):
“Permission denied (publickey).”
成功後再把私錀改回來
mv ~/.ssh/id_rsa.backup ~/.ssh/id_rsa

以 ssh-key 代替密碼登入遠端機器

muchu@glory:~$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/muchu/.ssh/id_rsa): /home/muchu/id_rsa
Enter passphrase (empty for no passphrase): #直接按 enter
Enter same passphrase again: #直接按 enter
Your identification has been saved in /home/muchu/id_rsa.
Your public key has been saved in /home/muchu/id_rsa.pub.
The key fingerprint is:
21:02:7e:b1:7d:11:85:26:e7:4b:16:ae:39:d5:79:ed muchu@glory
The key's randomart image is:
+--[ RSA 2048]----+
| . . o+. |
| . . +. =. |
| . + o*oo . . |
| . . o*.o . . |
| *S. . . |
| + . E |
| . |
| |
| |
+-----------------+
muchu@glory:~$ ls /home/muchu | grep id_rsa
id_rsa id_rsa.pub

id_rsa 是私錀,id_rsa.pub 是公錀。
將 "公錀的內容" 附加在 "對方" 主機的 ~/.ssh/authorized_keys 下。
並將私錀以 id_rsa 的名稱放在 ~/.ssh/ 下。就可以不用密碼 ssh 登入 "對方" 主機。

在 debian 系統,放公錀到對方主機的動作可以透過以下命令完成,先將公錀放在 ~/.ssh/ 下
ssh-copy-id muchu@www

2009年10月19日 星期一

trac 重新同步 svn

如果在 trac 已經指定了某個 svn repository 的環境下,
重新建立 svn repository 導致 repositoy 版本變動,如:
將 rev 101 僅 dump 出第 101 版本,並放棄第 1~100 版。
重新建立 reposiotry load 101 版為新 repository 的第 1 版。
這時連到原本指定 repository 的 trac 時會看到以下的錯誤訊息:

Can't synchronize with the repository

執行以下命令重新進行 trac 與 svn 的同步即可解決:

trac-admin </path/to/projenv> resync

2009年10月12日 星期一

trac 備份及還原

備份
trac-admin /usr/local/trac/wiki hotcopy ~/backup/wiki
還原
cp 回去即可

apache2 + svn + trac 設置

一、apache2 + svn
#安裝 svn apache2 及 apache2-svn 模組
apt-get install apache2 subversion libapache2-svn
#建立 svn 目錄
mkdir -p /usr/local/svn
#建立 repos svn倉庫
svnadmin /usr/local/svn/repos
#改變 repos 存取權限
chown -R www-data /usr/local/svn/repos
#設定 apache dav_svn 模組
vim /etc/apache2/mods-available/dav_svn.conf
#修改為
<Location /svn>
DAV svn
SVNPath /usr/local/svn/repos
AuthType Basic
AuthName "Subversion repos"
AuthUserFile /etc/apache2/dav_svn.passwd
AuthzSVNAccessFile /etc/apache2/dav_svn.authz
#<LimitExcept GET PROPFIND OPTIONS REPORT>
Require valid-user
#</LimitExcept>
</Location>
#建立 dav_svn.authz 設定帳號讀寫權限
vim /etc/apache2/dav_svn.authz
#修改為 :設定 "/" = /repos 下的資料除了 muchu 帳號可以讀寫,其他帳號均不可讀寫。
[/]
* =
muchu = rw
#建立 dav_svn.passwd 設定帳號密碼 (如果檔案已存在就不需要 -c)
/usr/bin/htpasswd -c /etc/apache2/dav_svn.passwd muchu
#重新啟動 apache2
/etc/init.d/apache2 restart
#以 http co svn
svn co http://www.bennu.tw/svn

二、trac
#安裝 trac 及 mod_python 模組
apt-get install trac libapache2-mod-python
#建立 trac 根目錄
mkdir -p /usr/local/trac
#初始化 trac
trac-admin /usr/local/trac/wiki initenv
#Project Name 指定為
wiki
#Path to Repository 指定為
/usr/local/svn/repos
#設定 trac.ini
vim /usr/local/trac/wiki/conf/trac.ini
#修改 default_charset 編碼
default_charset = utf-8
#修改存取權限
chown www-data.www-data -R /usr/local/trac/wiki
# standalone 執行測試
sudo -u www-data tracd --port 8000 /usr/local/trac/wiki
存取 http://127.0.0.1:8000/ 檢查是否正常運作
#建立 apache2 Virtual Host
vim /etc/apache2/sites-available/trac
#修改為:
<VirtualHost *:80>
ServerName trac.bennu.tw
ServerAdmin muchu@trac.bennu.tw

DocumentRoot /usr/local/trac

ErrorLog /var/log/apache2/trac-error.log
CustomLog /var/log/apache2/trac-access.log combined

SetHandler mod_python
PythonHandler trac.web.modpython_frontend
PythonOption TracEnv "/usr/local/trac/wiki"
PythonOption TracUriRoot "/trac/wiki"
<LocationMatch "/trac/[^/]+/login">
AuthType Basic
AuthName "trac"
AuthUserFile /etc/apache2/dav_svn.passwd
Require valid-user
</LocationMatch>
</VirtualHost>
#啟動 trac Virtual Site
sudo a2ensite trac
#重啟 apache2
/etc/init.d/apache2 restart
#新增 trac.bennu.tw domain
trac CNAME www.bennu.tw.
#存取 http://trac.bennu.tw/trac/wiki
並可以 svn 使用的 muchu 帳號登入

2009年10月2日 星期五

python 的第三方模組安裝 (windows)

直接將第三方的模組:如 eazygui.py 放到 C:\Python26\Lib\site-packages 下即可。

2009年10月1日 星期四

python 學習資料小集

Python 教學文件
http://www.python.tw/ebook/Guido%20van%20Rossum/tut.html
Python 學習手冊
http://www.oreilly.com.tw/product2_c.php?id=a240
易記學
http://ez2learn.com/index.php/python-tutorials
pydoing
http://pydoing.blogspot.com/
官方 module index
http://docs.python.org/modindex.html

2009年8月28日 星期五

java JVM 增大記憶體配置參數 -X 與 -XX

java OOM 分為兩種:

java.lang.OutOfMemoryError: PermGen space
表示 stack 空間不夠,可能是 static 變數太多,或 class 太多。

java.lang.OutOfMemoryError: Java heap space
表示 heap 空間不夠,可能是建立的物件太多,或是處理資料檔案太大。

-Xms<size> : 初始的記憶體大小 (heap)
-Xmx<size> : 最大的記憶體大小 (heap)
-XX:MaxNewSize=<size> : 初始的記憶體大小 (stack)
-XX:MaxPermSize=<size> :最大的記憶體大小 (stack)
-Xincgc = 讓gc在背景持續執行

例:
java -Xms512M -Xmx1024M -XX:MaxNewSize=256m -XX:MaxPermSize=256m -Xincgc -version

2009年8月21日 星期五

spring 的 annotation @Value 介紹

通常在 bean.xml 裡我們常以 value 注入單純的資料,如 int,string、
也會注入 properties 檔裡的變數資料,如:
x-setting.properties -
com.transtep.xdna.doc.projplan.docDirLocation=tpma_doc

x-bean.xml -
<property name="docDirLocation">
<value>${com.transtep.xdna.doc.projplan.docDirLocation}</value>
</property>

但spring 3.0 使用 annotation 的時候,x-bean.xml 有些 bean 就不需要再宣告了,如 @Controller
而是自動讓 spring 去 scan 某 package 找到的,如此一來就無法注入 value
在 spring 3.0.M3 時加入了 @Value 這一個方便的 annotation 讓我們可以重拾 value 注入。
用法:

@Value("${com.transtep.xdna.doc.projplan.docDirLocation}")
private String docDirLocation = "";

2009年8月9日 星期日

svn merge 合併 用法理解

svn merge 是用來合併 "其他" repos目錄的某段版本之間的 diff 到你指定的目錄(一般為 . )
假設 有 trunk/stable 和 branches/maintain 兩個目錄都是同一個專案的程式碼,

直接以範例來解釋:
一開始,在trunk下已經有一個穩定版本,但是在 ver 27 時發現一個 bug,
於是將 stable 以 svn copy 到 branches/maintain 並 commit 準備進行修正,此時,
ver 28 時 trunk/stable 和 branches/maintain 均為同一版本的程式碼。

maintain 開始修正 bug ...
在 bug 修正的過程中,一共 commit 了10個版本,並將 bug修正完成。
同時 stable 也有做一些新功能開發,但和 maintain 正在修正的 bug 無太大關聯。

此時 ver為 38,但 maintain 的程式碼和 stable 的程式碼,
已經有 28->38 之間 10個版本的落差,

為了將 maintain 做的修正 "補回" 到 trunk 中,我們要做 merge,首先 cd 到 stable 目錄下
執行 svn merge -r 28:38 ../../branches/maintain/ .
結果即是將 maintain 在版本 28->38 之間做的變更(diff) ,合併到目前的 stable 目錄下。

此階段也許會產生衝突,因為trunk也有可能和 maintain 在同一段程式碼有做修改。
解決衝突後,stable的版本就會是 maintain bug 修正 加 stable 新功能開發版本,
但是 "尚未 commit"。
再進行 stable 的 commit 後,stable 即是最新的版本。

2009年7月31日 星期五

java ee servlet response

HttpServletResponse 物件是用來傳回結果到 browser,有些方法常用的方法:

設定 mime type:以下為 open office 的 odt 文件的 mime type
res.setContentType("application/vnd.oasis.opendocument.text");
設定編碼:
res.setCharacterEncoding("utf-8");
設定元件檔名:
res.addHeader("Content-Disposition", "attachment; filename=project-plan-book.odt");

2009年7月22日 星期三

hql 結合 distinct 過瀘重復的查詢結果

有些情況下,會有查詢到重復物件的問題,
舉一個例子直接說明:
A -> B :1 -> 多
from A as a left join fetch a.b as b
由於A與B是一對多的關係,即許多個B可能以外鍵指向同一個A
以上的hql查詢結果會返回許多重覆的A物件,
假設某A有3個B指向他,則會返回3個某A物件。
這時可以透過distinct將重復的A物件去除,如下:
select distinct a from A as a left join fetch a.b as b

2009年7月6日 星期一

debian 簡易檔案加密解密 ccrypt

安裝:
sudo aptitude install ccrypt

加密:
ccencrypt target-file
Enter encryption key:
Enter encryption key: (repeat)
target-file -> 變成 target-file.cpt 檔案

解密:
ccdecrypt target-file.cpt
Enter decryption key:
target-file.cpt -> 變成 target-file 檔案

顯示加密檔的內容但不解密:
ccat target-file.cpt
Enter decryption key:
.....秘密的資料內容....#@$
target-file.cpt 維持不變。

2009年7月2日 星期四

使用JFreeChart畫甘特圖(gantt)範例

JFreeChart主要分為 JFreeChart 物件表示圖表 及 Dataset 物件表示資料
不同的圖表物由 ChartFactory 不同的 static 方法建立,而不同的圖表物件也需要不同的 Dataset
來儲存資料,如甘特圖的 Dataset 為 IntervalCategoryDataset :

/**
*主程式進入點
*/
public static void main(String[] args){

final IntervalCategoryDataset dataset = createFakeDataset();
final JFreeChart chart = createChart(dataset);
// 輸出圖表成png檔(如果是web servlet可以輸出到response.getOutputStream())
ChartUtilities.writeChartAsPNG(target.getOutputStream(), outputChart, 2000, 600);
}

/**
* 建立甘特圖資料
*
* @return The dataset.
*/
public IntervalCategoryDataset createDataset() {

final TaskSeries s1 = new TaskSeries("計劃");
s1.add(new Task("許功蓋中文測試", new SimpleTimePeriod(date(1, Calendar.APRIL, 2001), date(5, Calendar.APRIL, 2001))));
s1.add(new Task("Obtain Approval", new SimpleTimePeriod(date(9, Calendar.APRIL, 2001), date(9, Calendar.APRIL, 2001))));
s1.add(new Task("Requirements Analysis", new SimpleTimePeriod(date(10, Calendar.APRIL, 2001), date(5, Calendar.MAY, 2001))));
s1.add(new Task("Design Phase", new SimpleTimePeriod(date(6, Calendar.MAY, 2001), date(30, Calendar.MAY, 2001))));
s1.add(new Task("Design Signoff", new SimpleTimePeriod(date(2, Calendar.JUNE, 2001), date(2, Calendar.JUNE, 2001))));
s1.add(new Task("Alpha Implementation", new SimpleTimePeriod(date(3, Calendar.JUNE, 2001), date(31, Calendar.JULY, 2001))));
s1.add(new Task("Design Review", new SimpleTimePeriod(date(1, Calendar.AUGUST, 2001), date(8, Calendar.AUGUST, 2001))));
s1.add(new Task("Revised Design Signoff", new SimpleTimePeriod(date(10, Calendar.AUGUST, 2001), date(10, Calendar.AUGUST, 2001))));
s1.add(new Task("Beta Implementation", new SimpleTimePeriod(date(12, Calendar.AUGUST, 2001), date(12, Calendar.SEPTEMBER, 2001))));
s1.add(new Task("Testing", new SimpleTimePeriod(date(13, Calendar.SEPTEMBER, 2001), date(31, Calendar.OCTOBER, 2001))));
s1.add(new Task("Final Implementation", new SimpleTimePeriod(date(1, Calendar.NOVEMBER, 2001), date(15, Calendar.NOVEMBER, 2001))));
s1.add(new Task("Signoff", new SimpleTimePeriod(date(28, Calendar.NOVEMBER, 2001), date(30, Calendar.NOVEMBER, 2001))));

final TaskSeries s2 = new TaskSeries("實際");
s2.add(new Task("許功蓋中文測試", new SimpleTimePeriod(date(1, Calendar.APRIL, 2001), date(5, Calendar.APRIL, 2001))));
s2.add(new Task("Obtain Approval", new SimpleTimePeriod(date(9, Calendar.APRIL, 2001), date(9, Calendar.APRIL, 2001))));
s2.add(new Task("Requirements Analysis", new SimpleTimePeriod(date(10, Calendar.APRIL, 2001), date(15, Calendar.MAY, 2001))));
s2.add(new Task("Design Phase", new SimpleTimePeriod(date(15, Calendar.MAY, 2001), date(17, Calendar.JUNE, 2001))));
s2.add(new Task("Design Signoff", new SimpleTimePeriod(date(30, Calendar.JUNE, 2001), date(30, Calendar.JUNE, 2001))));
s2.add(new Task("Alpha Implementation", new SimpleTimePeriod(date(1, Calendar.JULY, 2001), date(12, Calendar.SEPTEMBER, 2001))));
s2.add(new Task("Design Review", new SimpleTimePeriod(date(12, Calendar.SEPTEMBER, 2001), date(22, Calendar.SEPTEMBER, 2001))));
s2.add(new Task("Revised Design Signoff", new SimpleTimePeriod(date(25, Calendar.SEPTEMBER, 2001), date(27, Calendar.SEPTEMBER, 2001))));
s2.add(new Task("Beta Implementation", new SimpleTimePeriod(date(27, Calendar.SEPTEMBER, 2001), date(30, Calendar.OCTOBER, 2001))));
s2.add(new Task("Testing", new SimpleTimePeriod(date(31, Calendar.OCTOBER, 2001), date(17, Calendar.NOVEMBER, 2001))));
s2.add(new Task("Final Implementation", new SimpleTimePeriod(date(18, Calendar.NOVEMBER, 2001), date(5, Calendar.DECEMBER, 2001))));
s2.add(new Task("Signoff", new SimpleTimePeriod(date(10, Calendar.DECEMBER, 2001), date(11, Calendar.DECEMBER, 2001))));

final TaskSeriesCollection collection = new TaskSeriesCollection();
collection.add(s1);
collection.add(s2);

return collection;
}

/**
* 方便建立日期
*
* @param day
* the date.
* @param month
* the month.
* @param year
* the year.
*
* @return a date.
*/
public Date date(final int day, final int month, final int year) {
Calendar calendar = Calendar.getInstance();
calendar.set(year, month, day);
Date result = calendar.getTime();
return result;
}

/**
* 產生甘特圖物件
*
* @param dataset
* the dataset.
*
* @return The chart.
*/
public JFreeChart createChart(final IntervalCategoryDataset dataset) {
final JFreeChart chart = ChartFactory.createGanttChart("測試甘特圖 Demo", // chart title
"事件", // domain axis label
"日期", // range axis label
dataset, // data
true, // include legend
true, // tooltips
false // urls
);
//設定各處的字型(需設定為支援中文的字型,才不會造成中文亂碼)
Font font = new Font("標楷體", Font.BOLD, 16);
chart.getLegend().setItemFont(font);
chart.getTitle().setFont(font);
chart.getCategoryPlot().getDomainAxis().setLabelFont(font);
chart.getCategoryPlot().getDomainAxis().setTickLabelFont(font);
chart.getCategoryPlot().getRangeAxis().setLabelFont(font);
chart.getCategoryPlot().getRangeAxis().setTickLabelFont(font);
return chart;
}

結果如下(web)

2009年6月26日 星期五

hibernate order by

假設 Account 的主鍵是 username VARCHAR,如果想要列出所有帳號資料,並依主鍵排序的話。
可以使用 id 進行排序即可,不一定要指定 username,如:

from Account as acc order by acc.id

這樣的寫法等同於

from Account as acc order by acc.username

但由於以 id 排序,不需要知道特定表格的主鍵 column 的名稱,
在某些無法得知 column 名稱的場合,可以派得上用場