練功房推薦書單

  • Google!Android 3手機應用程式設計入門(第四版)
  • 賈伯斯傳(軟皮精裝版)
  • 猛虎出閘制霸版:最新OCP Java SE 6 Programmer專業認證(附原始程式碼及範例檔)
  • SCWCD 5 猛虎出閘:Java Web 應用程式專業認證
Messages posted by: andowson
Forum Index » Profile for andowson » Messages posted by andowson
Message
只要在/etc/postfix/main.cf中加上
relayhost = relay SMTP主機的IP或Domain Name
然後重新啟動postfix即可
service postfix restart

參考資料:
http://www.gungeralv.org/notes/archives/2003/06/howto_configure_postfix_to_use_a_remote_smtp_relay_host.php
Trac的刪除、備份跟還原
1.刪除Trac專案:
在Trac中每個專案環境就是一個目錄,所以刪除專案很簡單,就是把對應的目錄刪除掉,然後對應的資料庫也刪除掉即可。

2.備份Trac專案:
可以使用trac-admin的hotcopy指令,
$ trac-admin /path/to/projenv hotcopy /path/to/backupdir

例如:
[andowson@www ~]$ trac-admin /home/andowson/projects/webmail hotcopy /tmp/webmail
Hotcopying /home/andowson/projects/webmail to /tmp/webmail ... Hotcopy done.

檢視一下內容可以發現其實就是整個目錄複製過去而已,所以理論上只要先把Web Server停止後,自己使用OS的copy指令也是OK的
[andowson@www ~]$ ll /tmp/webmail/
總計 36
drwxrwxr-x 2 andowson andowson 4096 12月 21 20:09 attachments
drwxrwxr-x 2 andowson andowson 4096 12月 21 20:09 conf
drwxrwxr-x 2 andowson andowson 4096 12月 21 20:09 htdocs
drwxrwxr-x 2 andowson andowson 4096 12月 21 20:09 log
drwxrwxr-x 2 andowson andowson 4096 12月 21 20:09 plugins
-rw-r--r-- 1 andowson andowson 98 9月 9 2007 README
drwxrwxr-x 2 andowson andowson 4096 12月 21 20:09 templates
-rw-r--r-- 1 andowson andowson 27 9月 9 2007 VERSION
drwxrwxr-x 2 andowson andowson 4096 12月 21 20:09 wiki-macros

資料庫部分可以使用資料庫備份的指令,例如PostgreSQL的pg_dump

3.還原Trac專案
也是很簡單,就是把備份的目錄覆蓋回去即可,先把Web Server停掉,然後把整個目錄結構copy回去,再把資料庫還原回去,然後再重起Web Server即可。

Subverson的備份跟還原
1.備份Subversion的repository
svnadmin hotcopy /path/to/reponame /path/to/backupdir

例如:
svnadmin hotcopy /home/andowson/repos/webmail /tmp/webmailrepo
這個指令單純的將repository的目錄複製一份出來

2.還原Subversion的repository
只要將先前用hotcopy備份的目錄複製回去即可

參考資料:
http://trac.edgewall.org/wiki/TracBackup
http://svnbook.red-bean.com/en/1.1/ch05s03.html
http://wiki.archlinux.org/index.php/Subversion_backup_and_restore
http://rory.cba.tw/rewrite.php/read-95.html
首先,JForum的手動啟用帳號中輸入的是會員ID(整數),不是會員名稱(字串),您的會員ID可以由會員列表前面第一行數字查得(即#那一行)。
第二,驗證碼在當初加入會員時,已發送通知信到您註冊時所使用的電子郵件信箱,如果當初您註冊用的信箱是正確的,您可以到您的信箱中再找找看有沒有那封信。
第三,如果當初註冊時沒收到啟用帳號通知信,目前這個版本的JForum也沒有提供補發驗證碼的功能,如果您比較偏好使用fly這個帳號,有幾種作法:
a.由系統管理者手動啟用帳號,這樣子您會有兩個帳號
b.由系統管理者變更您目前通過認證的這個帳號名稱為fly,將原來的fly改為別的名稱
c.開發補發驗證碼的程式,可以模仿忘記密碼的程式來改,然後您再去申請補發驗證碼,然後再來啟用帳號。目前想到的邏輯如下:
1.在手動啟用帳號的頁面上加入補發驗證碼連結,使用者按下後顯示申請表單畫面
2.使用者同時輸入帳號跟新的電子郵件信箱地址
3.系統檢查該帳號是否已啟用
3.1.如果尚未啟用,則更新該帳號的電子郵件信箱,同時產生新驗證碼,寄送至這個新的電子郵件信箱
3.2如果已啟用,則顯示帳號已啟用畫面(避免帳號被盜用)
4.使用者至其新的電子郵件信箱收信,收到信後再啟用帳號

這幾個作法的優缺點如下:
a方案:優點:簡單,目前系統所提供的功能。缺點:無法確認使用者所填寫之電子郵件信箱是否正確,失去驗證碼機制所要的目的。然後同一個人變成有兩個帳號。
b方案:優點:可以保留目前您這個帳號所發表的文章紀錄。缺點:就是要請系統管理者去處理這件事,還有原先的文章可能會看起來怪怪的。
c方案:優點:可以讓使用者自行處理這件事情。不需要系統管理者介入。也可以回收一些帳號。缺點:就是還要開發程式,需要考量所花的時間成本以及日後與新版本程式相容性的問題。還有就是同一個人還是有兩個帳號。

目前,我比較傾向使用b方案。不知道您是否同意這樣的處理方式?
您的問題應該是因為帳號尚未啟用,所以無法通過登入驗證程序,主要是因為在net.jforum.sso.DefaultLoginAuthenticator這個類別的validateLogin方法裡面對於登入成功的條件是
		if (user != null && !user.isDeleted() && (user.getActivationKey() == null || user.isActive())) {
			return user;
		}

所以雖然透過忘記密碼功能可以成功修改密碼,但是如果您一直沒有去啟用帳號,則仍然無法登入成功。
使用PostgreSQL資料庫一段時間後,想瞭解一下資料庫目前的大小,可以透過以下的指令來查詢:
smilie PostgreSQL 8.1以後的版本:
psql db_name  -- db_name can be any database

SELECT pg_database.datname,
pg_size_pretty(pg_database_size(pg_database.datname)) AS size
FROM pg_database;

顯示的結果類似如下:
datname | size
-----------+---------
postgres | 3537 kB
support | 99 MB
jtrac | 8497 kB
template1 | 3480 kB
template0 | 3480 kB

注意事項:pg_database_size()需PostgreSQL 8.1以上才有支援

smilie PostgreSQL7.4以前的版本
因每個資料庫都有一個oid,而每個oid都有一個對應的目錄名稱,先用下面的指令找出資料庫的oid
psql db_name  -- db_name can be any database

select oid,datname from pg_database;

oid | datname
--------+----------------
1 | template1
17141 | template0
20744 | forum
474667 | jtrac
498681 | trac_onlinemsg
499009 | trac_webmail
509099 | olat
511373 | testdb
514460 | roller

然後切換目錄到/var/lib/pgsql/data/base下
cd /var/lib/pgsql/data/base
使用du --si來取得使用硬碟空間的大小
-bash-3.00$ du --si
4.7M ./511373==>testdb
5.3M ./474667==>trac
4.7M ./1==>template1
5.8M ./498681==>trac_onlinemsg
31M ./514460==>roller
7.2M ./509099==>olat
5.9M ./499009==>trac_webmail
4.1k ./20744/pgsql_tmp
131M ./20744==>forum
4.6M ./17141==>template0

參考資料:
http://www.synack.info/how-big-is-postgresql-database-size/
http://mi.nws.noaa.gov/oh/hrl/hseb/postgreSQL/Creating_Database.doc
這篇文章寫得不錯,How to optimize PostgreSQL database size
作者比較了三種縮小資料庫佔用硬碟空間的方法(衡量指標是耗用時間跟佔用硬碟空間大小)
vacuumdb/reindexdb: 190min - 1990MB
reindexdb/vacuumdb/reindexdb: 105min - 1990MB
dump/dropdb/createdb/restore: 20min - 1685MB
最後看起來應該是採用第三種方法比較快速有效,不過這三種方法都需要先將資料庫先離線才行。
fly這個帳號目前尚未啟用,不知道是您未收到確認信還是信箱輸入錯誤,如果是後者,那就無解了。
有關忘記密碼部分,請參考這篇的說明
http://www.andowson.com/posts/list/291.page
有關JForum忘記密碼的功能是正常的
剛測試了一下,它的運作流程大致如下
1.點選登入
2.點選忘記密碼
3.輸入原來註冊時所用的電子郵件信箱或會員名稱任何一個,然後按下確定。
4.JForum系統發送一封電子郵件至您當初註冊的電子郵件信箱,然後顯示一個完成畫面,請您點選某個連結回到登入頁面
=>最初註冊時填寫的電子郵件信箱必須正確否則您永遠收不到信
5.進入到您的電子郵件信箱,您會看到一封主旨為補發密碼通知的信件,裡面有一個URL,類似下方這樣:
http://www.andowson.com/user/recoverPassword/b5b466d022a4be46b9f4530cb7a212ce.page
6.將上面的網址複製並貼到原來瀏覽器視窗的網址列,然後按下Enter
7.再輸入一次您的原來註冊時填的電子郵件信箱及新的密碼兩次,按下確定
8.JForum確認無誤後,更改您的密碼,然後顯示一個完成頁面,請您點選某個連結回到登入頁面
=>上面通知信內的URL連結只能使用一次,如果日後您又忘記了,請重複一次上述流程
9.點選登入連結後,輸入帳號跟密碼,按下登入,然後...
出現404錯誤,找不到網頁?

原因是這個登入畫面裡面有個隱藏欄位returnPath,紀錄呼叫這個登入畫面的來源網址,以便在進行完帳號密碼驗證後導回去剛剛的地方。檢視畫面原始碼可以發現,這時變成如下:
<input type="hidden" name="returnPath" value="http://www.andowson.com/jforum.page" />
而原本JForum的設計, *.page 通通是交給 jforum 這個Controller Servlet來處理,但是如果沒有傳任何參數給它,就會變成moduleClass == null的條件成立,而預設是回傳404錯誤碼。我稍微改了一下JForum.java的service()方法,讓它導回根目錄:
			if (moduleClass == null) {
				// Module not found, send 404 not found response
				//response.sendError(HttpServletResponse.SC_NOT_FOUND);
				response.sendRedirect("/");
			}

如此一來,就OK了。
關於gif部分,我改良了一下lynx286的修改方式:
AttachmentCommon.java:
將最大寬度跟高度改為由系統參數檔設定
	private boolean shouldCreateThumb(Attachment a) {
		String extension = a.getInfo().getExtension().getExtension().toLowerCase();
		if (SystemGlobals.getBoolValue(ConfigKeys.ATTACHMENTS_IMAGES_CREATE_THUMB)
            && Attachment.isPicture(extension)) {
            String path = SystemGlobals.getValue(ConfigKeys.ATTACHMENTS_STORE_DIR)
			     + "/" 
			     + a.getInfo().getPhysicalFilename();
		    File imageFile = new File(path);
		    BufferedImage image = null;
		    try {
			    image = ImageIO.read(imageFile);
		    } catch (IOException e) {
			    e.printStackTrace();
		    }
		    int width = image.getWidth(null);
		    int height = image.getHeight(null);
		    return (width > SystemGlobals.getIntValue(ConfigKeys.ATTACHMENTS_IMAGES_MAX_THUMB_W) 
		        || height > SystemGlobals.getIntValue(ConfigKeys.ATTACHMENTS_IMAGES_MAX_THUMB_H));
        }
        return false;
	}

Attachment.java:
精簡一下程式碼
	public boolean hasThumb() 
	{
		String filename = this.info.getPhysicalFilename();
		String extension = filename.substring(filename.lastIndexOf('.') + 1, 
				filename.length() - 1);
		return SystemGlobals.getBoolValue(ConfigKeys.ATTACHMENTS_IMAGES_CREATE_THUMB)
			&& isPicture(extension);
	}
	
	public String thumbPath() {
		String urlPath = SystemGlobals.getValue(ConfigKeys.ATTACHMENTS_UPLOAD_DIR)
		    + '/' + this.info.getPhysicalFilename();
		String realPath = SystemGlobals.getValue(ConfigKeys.ATTACHMENTS_STORE_DIR)
		    + "/" + this.info.getPhysicalFilename();
		if (new File(realPath + "_thumb").exists()) {
			return urlPath + "_thumb";
		} else {
			return urlPath;
		}    
	}
	
	public static boolean isPicture(String extension) {
		return ("jpg".equals(extension) || "jpeg".equals(extension) 
				|| "gif".equals(extension) || "png".equals(extension)
				|| "bmp".equals(extension));
	}

UserCommon.java:
增加刪除舊的個人頭像,比對用的最大寬度跟高度調整為以系統設定值為準,及將過大的gif轉為png
	private static void handleAvatar(User u)
	{
		// Delete old avatar file
		if (u.getAvatar() != null) {
			File avatarFile = new File(u.getAvatar());
			
			File fileToDelete = new File(SystemGlobals.getApplicationPath() 
				+ "/images/avatar/"
				+ avatarFile.getName());
			
			if (fileToDelete.exists()) {
				fileToDelete.delete();
			}
		}
		
		String fileName = MD5.crypt(Integer.toString(u.getId()));
		FileItem item = (FileItem)JForumExecutionContext.getRequest().getObjectParameter("avatar");
		UploadUtils uploadUtils = new UploadUtils(item);
		
		// Gets file extension
		String extension = uploadUtils.getExtension().toLowerCase();
		int type = ImageUtils.IMAGE_UNKNOWN;
		
		if (extension.equals("jpg") || extension.equals("jpeg")) {
			type = ImageUtils.IMAGE_JPEG;
		} else if (extension.equals("gif")) {  
			type = ImageUtils.IMAGE_GIF;  
		} else if (extension.equals("png")) {  
			type = ImageUtils.IMAGE_PNG;  
		}
		
		if (type != ImageUtils.IMAGE_UNKNOWN) {
			String avatarTmpFileName = SystemGlobals.getApplicationPath() 
				+ "/images/avatar/" 
				+ fileName 
				+ "_tmp." 
				+ extension;
	
			String avatarFinalFileName = SystemGlobals.getApplicationPath() 
				+ "/images/avatar/" 
				+ fileName 
				+ "." 
				+ extension;
	
			uploadUtils.saveUploadedFile(avatarTmpFileName);		

			// OK, time to check and process the avatar size
			int maxWidth = SystemGlobals.getIntValue(ConfigKeys.AVATAR_MAX_WIDTH);
			int maxHeight = SystemGlobals.getIntValue(ConfigKeys.AVATAR_MAX_HEIGHT);

			File avatar = new File(avatarTmpFileName); 
			BufferedImage imageOriginal = null;
			try {
				imageOriginal = ImageIO.read(avatar);
			} catch (IOException e) {
				e.printStackTrace();
			}
			int width = imageOriginal.getWidth(null);
			int height = imageOriginal.getHeight(null);

			if (width > maxWidth || height > maxHeight) {				
				if (type == ImageUtils.IMAGE_GIF) {
					type = ImageUtils.IMAGE_PNG;
					extension = "png";
				}
				BufferedImage image = ImageUtils.resizeImage(avatarTmpFileName, type, maxWidth, maxHeight);				
				ImageUtils.saveImage(image, avatarFinalFileName, type);
				// Delete the temporary file
				avatar.delete();
			} else {
				avatar.renameTo(new File(avatarFinalFileName));
			}
			u.setAvatar(fileName + "." + extension);
		}
	}
原本JForum的設計是把所有上傳的圖片,縮小到系統設定的大小,作為縮圖。可是gif格式的部分卻是轉成png。
目前有幾個方法來解:
第一種是跳過gif檔案,只要遇到gif圖片,就保留原始檔案,其他的格式繼續進行縮放,可以參考lynx286所發表的這篇http://www.andowson.com/posts/list/253.page
第二種是真的去將gif圖片縮放為gif圖片,可以使用一些Gif Encoder的類別做做看,例如http://www.acme.com/java/software/Acme.JPM.Encoders.GifEncoder.html

目前我是採用第一種方式,附上我改好的這幾個檔案,如果懶得自己改的話,可以直接下載回去編譯即可。
第二種方法就看有沒有其他網友試出來之後,再分享給大家吧。
我是參考這篇lynx286所發表的方式:
http://www.andowson.com/posts/list/253.page
我也來測試看看
您可以試著調整一下PostgreSQL的最大連線數看看

/var/lib/pgsql/data/postgresql.conf
max_connections = 200

參考資料:
http://readthefuckingmanual.net/error/132/
最近公司要辦研討會,需要寫一個報名網頁,收集參加研討會人員的資料,然後每天下午五點時將報名資料寄送給相關人員,由他們再處理後續的準備事項,所以需要處理下列需求:
1.將資料庫中的報名資料匯出成為一個檔案,例如csv,
2.然後再利用郵件附件方式寄送出去。
3.定時自動執行

第一項任務,因我們使用PostgreSQL 8.3,所以可以使用COPY指令來完成,例如報名資料表名字叫作attendee,為避免寫入權限的問題,我們將其寫入到/tmp/attendee.csv下,同時將其表頭一併輸出(注意:這項功能需要PostgreSQL 8.1版本以上才支援)
copy attendee to '/tmp/attendee.csv' csv header

因為要使用COPY指令需要到postgres這個使用者的權限,故我們可以將上述指令存檔為export.sql存放到/var/lib/pgsql目錄下
然後再利用su方式來完成:
su - postgres -c "psql seminar postgres -f /var/lib/pgsql/export.sql"

第二項任務可利用mutt這個程式來執行
$ mutt -s "報名資料表" -a /tmp/attendee.csv pm@mycompany.com -c boss@mycompany.com,partner@other.com < /dev/null

如果沒有mutt可以透過yum -y install mutt來安裝

完整的shell script如下:
#!/bin/bash
su - postgres -c "psql seminar postgres -f /var/lib/pgsql/export.sql"
mutt -s "報名資料表" -a /tmp/attendee.csv pm@mycompany.com -c boss@mycompany.com,partner@other.com < /dev/null


第三項任務就是排定cron job即可,在/etc/crontab中設定如下
01 17 * * * root /root/dump.sh

參考資料:
Sending files as mail attachments
 
Forum Index » Profile for andowson » Messages posted by andowson
Go to: