AsyncWeb & load test
There is insufficient information for AsyncWeb (subproject of Mina) on internet,
what I need is simulating a web services that reply in various seconds for load testing.
the following code may not be correct, some wiered debug message…
and I just depressed it by log4j.properties
Mostly follow the lightweight example of AsyncWeb,
the eclipse project zip can be obtained here
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 26 27 28 29 30 31 32 33 |
public class Main { private static final int PORT = 80; /** * Main * * @param args * @throws IOException */ public static void main(String[] args) throws IOException { SocketAcceptor acceptor = new NioSocketAcceptor(); acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new HttpCodecFactory())); acceptor.getFilterChain().addLast("executor", new ExecutorFilter(new OrderedThreadPoolExecutor(32))); //Allow the port to be reused even if the socket is in TIME_WAIT state acceptor.setReuseAddress(true); acceptor.getSessionConfig().setReuseAddress(true); acceptor.getSessionConfig().setReceiveBufferSize(1024); acceptor.getSessionConfig().setSendBufferSize(1024); // No Nagle's algorithm acceptor.getSessionConfig().setTcpNoDelay(true); acceptor.getSessionConfig().setSoLinger(-1); acceptor.setBacklog(10240); acceptor.setHandler(new CustomHttpIoHandler()); // customer handler acceptor.bind(new InetSocketAddress(PORT)); } } |
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
public class CustomHttpIoHandler implements IoHandler { private final Logger logger = LoggerFactory.getLogger(getClass()); private final Timer timer; private Random random = new Random(System.currentTimeMillis()); private final String SOAPENV_HEAD = ""; public CustomHttpIoHandler(){ timer = new Timer(true); } public void exceptionCaught(IoSession session, Throwable cause) throws Exception { if (!(cause instanceof IOException)) { cause.printStackTrace(); } session.close(true); } public void messageSent(IoSession session, Object message) throws Exception { } public void sessionClosed(IoSession session) throws Exception { } public void sessionCreated(IoSession session) throws Exception { } public void sessionIdle(IoSession session, IdleStatus status) throws Exception { session.close(true); } public void sessionOpened(IoSession session) throws Exception { session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30); } public void messageReceived(IoSession session, Object message) throws Exception { HttpRequest req = (HttpRequest) message; doAsynchronousDelayedResponse(session, req); } /** * business logic for various percentage and behaviour * * @param session mina session * @param req Http request * @throws CharacterCodingException */ private void doAsynchronousDelayedResponse(final IoSession session, final HttpRequest req) throws CharacterCodingException { int delay = 0; Behaviour behaviour; int value = nextRandom(1, 100); if(value < = 50){ // 50% of responses return valid result within 0-3 secs (good response time & good result) delay = nextRandom(10, 3000); behaviour = Behaviour.GOOD_TIME_GOOD_RESULT; }else if(value > 50 && value < =60){ //10% of responses return expected error within 0-3 secs (good response time & expected error result) delay = nextRandom(10, 3000); behaviour = Behaviour.GOOD_TIME_ERROR_RESULT; }else if(value > 60 && value < = 90){ //30% of responses return valid result within 3-15 secs (fair response time & good result) delay = nextRandom(3000, 15000); behaviour = Behaviour.FAIR_TIME_GOOD_RESULT; }else if(value > 90 && value < =95){ //5% of responses return valid result within 15-190 secs (poor response time & good result) delay = nextRandom(15000, 190000); behaviour = Behaviour.POOR_TIME_GOOD_RESULT; }else if(value > 95 && value < =97){ //2.5% of responses return valid result after 190 seconds (bad response time & good result) delay = nextRandom(15000, 190000); behaviour = Behaviour.BAD_TIME_GOOD_RESULT; }else{ //2.5% of responses return 'no content' after 190 seconds (bad response time & bad result) delay = nextRandom(15000, 190000); behaviour = Behaviour.BAD_TIME_BAD_RESULT; } final MutableHttpResponse res = new DefaultHttpResponse(); res.setStatus(HttpResponseStatus.OK); res.setContentType("application/soap+xml; charset=utf-8"); // get corresponding response from behaviour StringBuffer sb = getResponseXML(behaviour); IoBuffer bb = IoBuffer.allocate(1024); bb.setAutoExpand(true); bb.putString(sb.toString(), Charset.forName("UTF-8").newEncoder()); bb.flip(); res.setContent(bb); logger.info("delay:" + delay + " stub:" + behaviour); timer.schedule(new TimerTask() { @Override public void run() { writeResponse(session, req, res); } }, delay); } /** * Write the http response * * @param session mina session * @param req HTTP Request * @param res HTTP Response */ private void writeResponse(IoSession session, HttpRequest req, MutableHttpResponse res) { res.normalize(req); WriteFuture future = session.write(res); if (!HttpHeaderConstants.VALUE_KEEP_ALIVE.equalsIgnoreCase( res.getHeader( HttpHeaderConstants.KEY_CONNECTION))) { future.addListener(IoFutureListener.CLOSE); } } /** * Generate next random number from min(inclusive) to max(inclusive) * @param min minimum number * @param max maximum number * @return random integer */ private int nextRandom(int min, int max){ return random.nextInt(max-min+1)+min; } /** * Indicate what kind of behaviour * * @author SteveChan */ public enum Behaviour{ GOOD_TIME_GOOD_RESULT, //return valid result within 0-3 secs GOOD_TIME_ERROR_RESULT, //return expected error within 0-3 secs FAIR_TIME_GOOD_RESULT, //return valid result within 3-15 secs POOR_TIME_GOOD_RESULT, //return valid result within 15-190 secs ( BAD_TIME_GOOD_RESULT, //return valid result after 190 seconds BAD_TIME_BAD_RESULT //return 'no content' after 190 seconds } /** * * @param stub indicate what Behaviour * @return StringBuffer with return xml content */ private StringBuffer getResponseXML(Behaviour stub){ StringBuffer sb = new StringBuffer(); if(stub.equals(Behaviour.GOOD_TIME_GOOD_RESULT) || stub.equals(Behaviour.FAIR_TIME_GOOD_RESULT) || stub.equals(Behaviour.POOR_TIME_GOOD_RESULT) || stub.equals(Behaviour.BAD_TIME_GOOD_RESULT)){ sb.append(SOAPENV_HEAD) .append("") // some content here .append("") .append(""); }else if(stub.equals(Behaviour.GOOD_TIME_ERROR_RESULT)){ sb.append(SOAPENV_HEAD) .append("") // some content here .append("") .append(""); }else{ //BAD_TIME_BAD_RESULT sb.append(SOAPENV_HEAD) .append("") // some content here .append("") .append(""); } if(logger.isDebugEnabled()){ logger.debug("return stardard soap response xml by " + stub ); logger.debug(sb.toString()); } return sb; } } |
batch script
因為唔同platfrom… 係linux/max 上可以行既shell script,
係windows 係可以用cygwin 去行
可惜, 如果用 eclipse > ant build , 個ant call *.sh 就出事
寫(translate)左一個好無聊既 batch script
(被)玩左一個下午,就係因為唔識寫script, 同埋個help 係 %~dpnI 唔係 %%~dpnI ….
記錄一下
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 26 27 28 29 30 31 32 33 |
::-----------------------------------------------------------:: :: CONFIGURATION SCRIPT :: :: Script copies file.type.{DEPLOY} to file.type :: Where {DEPLOY} is one of PRODUCTION|STAGING|QA|INTERNAL|DEV :: :: modify from config.sh to windows environment :: :: Author: Steve Chan steve.chan@pxxxxl.com.au :: $Id: config.bat,v 1.0 2009/01/07 13:24:57 steve Exp $ ::-----------------------------------------------------------:: :: check that we were supplied two parameters @ECHO OFF IF NOT (%1)==() SET DEST=%1 IF NOT (%2)==() SET DEPLOY=%2 ::@ECHO ON ECHO.DIR /b /s %DEST%*.%DEPLOY% :: copy all filenames with extension .<deploy> to a temporary file DIR /b /s %DEST%\*.%DEPLOY% > tmp.out :: iterate through the filenames and copy to the filename root FOR /f %%I IN (tmp.out) DO ( ECHO.copy: %%I %%~dpnI COPY %%I %%~dpnI ) :: get rid of the temporary file del tmp.out </deploy> |
part of maven.xml
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 26 27 28 29 30 31 32 |
<goal name="config"> <echo>CONFIGURATION BUNDLE USED: ${configuration.bundle}</echo> <echo>${configuration.script} ${basedir} ${configuration.bundle}</echo> <!-- distinguish windows environment, using config.bat instead of config.sh --> <!-- orginal script--> <!-- <ant:exec dir="${basedir}" executable="sh"> <ant :arg line="${configuration.script} ${basedir} ${configuration.bundle}"/> --> <ant :condition property="os.windows.family"> <ant :os family="windows"/> </ant> <j :choose> </j><j :when test="${os.windows.family}"> <ant :exec dir="${basedir}" executable="cmd"> <ant :arg line="/c config.bat ${basedir} ${configuration.bundle}" /> <echo>Windows Environment: config.bat ${basedir} ${configuration.bundle}</echo> </ant> </j> <j :otherwise> <ant :exec dir="${basedir}" executable="sh"> <ant :arg line="${configuration.script} ${basedir} ${configuration.bundle}" /> </ant> </j> <!-- end modification for windows environment --> </goal> |
個 :exec 食左, </ant:exec>
[Java] PDF to JPG
http://forum.java.sun.com/thread.jspa?threadID=359395&start=30&tstart=0
Here is a quick test sample that I ran with for PDF to JPG conversion.
I only had to convert the 1st page, so if you wanted multi-pages you will have to adapt the above example.
I use the ImageIO and jpedal jars.
Quick overview.
give the decoder the filename of the pdf to open
Grab the 1st page, and resize it to a thumbnail version
save the image.
import org.jpedal.*; import java.io.*; import javax.imageio.*; import javax.imageio.stream.*; import java.awt.Graphics; import java.awt.Image; import java.awt.image.BufferedImage; import java.awt.image.RenderedImage; import java.util.*; public class PdfToJpgTest { public static void PDFToJpg(String filepath) { try { PdfDecoder decoder = new PdfDecoder(); decoder.openPdfFile(filepath); if (decoder.isFileViewable()) { //just grab the first page, and render it as an image. BufferedImage thumbnailImage = getThumbnailImage(decoder.getPageAsImage(1)); saveImage(thumbnailImage,"1.jpg"); } } catch(Exception err) { err.printStackTrace(); } } public static void saveImage(Image image, String fileName) { File fileToSave = new File(fileName); try { ImageIO.write((RenderedImage)image,"jpg", fileToSave); } catch(Exception err) { err.printStackTrace(); } } private static BufferedImage getThumbnailImage(BufferedImage image) { BufferedImage resizedImage = null; Graphics g = image.getGraphics(); int resizeWidth = 0; int resizeHeight = 0; resizeHeight = 256; resizeWidth = (image.getWidth() * resizeHeight) / image.getHeight(); resizedImage = new BufferedImage(resizeWidth, resizeHeight, BufferedImage.TYPE_INT_RGB); resizedImage.createGraphics().drawImage(image, 0, 0, resizeWidth, resizeHeight, null); return resizedImage; } public static void main(String args[]) { System.out.println("Staring PDF to JPG test"); PDFToJpg("Winter 2006.pdf"); System.out.println("Ending PDF to JPG test"); } }
Jakarta Commons Logging 與 Log4j
在 Web 應用程式中使用 Jakarta Commons Logging 與 Log4j
蔡煥麟 Mar-25-2005 http://140.138.148.144/~s882515/blog/log4j.htm
摘要
簡單介紹 Jakarta Commons Logging 與 log4j 的用法,以範例說明如何在 Web 應用程式中使用 logging。
1. 簡介
在開發 Java 或 JSP 應用程式時,最簡單的除錯技巧就是呼叫 System.out.println() 將訊息印出來,或者將訊息顯示在網頁上,這類方式很簡單,但卻有以下缺點:
- 若將除錯資訊顯示在網頁上,會破壞應用程式的輸出畫面。
- 除錯時埋了一堆 println() 呼叫,除錯完畢或者產品交付之前必須一一找出來刪除。
- 有些問題不易在開發時期發現,或者臭蟲出現的時機不是固定的,這類問題如果用單純的 println() 方法來找,由於輸出的訊息可能非常多,又沒有分類,要花比較多時間去過濾跟尋找。
比較好的 logging 技術便可以解決以上的問題,而且讓我們在程式的任何地方都能輸出除錯訊息,輸出的裝置可以是螢幕、檔案、甚至是電子郵件。如果把除錯訊息記錄到檔案中,經由檢視客戶執行時的記錄檔,就可以了解使用者在執行程式時所發生的內部細節,以便找出不容易在開發時期發現的臭蟲。
本文先簡單介紹 Jakarta Commons Logging 與 log4j 這兩個 logging 套件,並以 Web 應用程式的範例來說明如何利用這兩個套件輸出 log 訊息,包括安裝、設定、以及如何撰寫輸出 log 訊息的程式碼。
1.1 Jakarta Commons Logging 套件
Commons Logging 套件是一套通用的 logging API,可以讓開發人員隨意切換 logging 的實作產品,也就是說,如果你發現了一個更好用 logging 元件,你可以經由修改組態檔來切換欲使用的 logging 元件,而不用修改程式碼。
Commons Logging 套件支援的 logging 實作產品有:
- Log4j
- JDK 1.4 Logging
- Avalon LogKit
- SimpleLog(把日誌訊息寫到 stdout 和 stderr)
- NoOpLog(忽略日誌訊息)
其中 SimpleLog 和 NoOpLog 是 Commons Logging 內建的實作品。Log4j 這個實作品已經提供了很多功能,但如果你覺得現有的實作品仍無法滿足你的需求,你也可以自行實作,並為此實作品提供一個配接器(adapter),這樣你在程式中一樣還是使用 Commons Logging API 來撰寫 logging 程式碼。
1.2 Jakarta Log4j 套件
Log4j 是 Jakarta 開放原始碼計劃的其中一項專案,它是一個用來輸出日誌訊息的 Java 套件,它擁有豐富的功能,而且在設計上同時考慮到彈性與效率,使用也很容易。
如果你單獨使用 log4j,在程式裡面呼叫的日誌輸出函式就是 log4j 提供的專屬函式,但為了降低程式與特定 logging 元件的耦合度,比較好的方式還是使用 Commons Logging 搭配 log4j。
2. 安裝
以下是 Commons Logging 與 log4j 的安裝與設定步驟,主要是針對 Web 應用程式來說明:
- 安裝 Commons Logging 套件。到 http://jakarta.apache.org/site/downloads/downloads_commons.html 下載 Commons Logging 套件。我下載的檔案名稱是 commons-logging-1.0.4.zip。把壓縮檔裡面的所有 .jar 檔放到你的 Web 應用程式的 WEB-INF/lib 目錄下。
- 安裝 Log4j 套件。到 http://logging.apache.org/site/binindex.html 下載,我下載的檔案是 logging-log4j-1.2.9.zip。把 .jar 檔放到你的 Web 應用程式的 WEB-INF/lib 目錄下。
| 如果是一般的 Java 應用程式,可以把 .jar 檔案複製到 JDK 的 jrelibext 目錄下,或者將 .jar 檔的路徑加入 CLASSATH。 |
3. 使用
如前面提到的,Commons Logging 支援多種 logging 實作產品,這裡我們使用 Log4j 作為 logging 元件。
3.1 設定 Commons Logging 與 Log4j
在撰寫程式前,必須先建立一些設定檔,步驟如下:
- 在你的 Web 應用程式的 WEB-INFclasses 目錄下建立一個 commons-logging.properties 屬性檔。內容只有下面這行:
org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
這行的作用是指定 Commons Logging 使用的 logging 元件,這裡使用的是 log4j 套件的 Log4JLogger。
註: 有些文件是使用 org.apache.commons.logging.impl.Log4JCategoryLog,但此類別已經過時,請改用 Log4JLogger。
其它支援的 log 實作元件:
- org.apache.commons.logging.impl.SimpleLog – 這是 Commons Logging 內建的簡單 logger。
- org.apache.commons.logging.impl.Jdk14Logger – 這是 JDK 1.4 的 logger。
- 在你的 Web 應用程式的 WEB-INFclasses 目錄下建立一個 log4j.properties,內容如下:
log4j.rootLogger=INFO, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
此設定檔指定使用 console appender 來記錄日誌,以便將日誌訊息輸出到 sdtout(System.out)。你可以在這個檔案裡面定義多個 appender,也就是說,你在程式中輸出的 log 訊息都會輸出到這些 appender(或者說,都會使用這些 appender 來輸出訊息)。第 3 行是指定 layout 樣式,你可以自己指定輸出的訊息樣式,至於其格式和其中各參數代表的意義,請參考 log4j 的說明文件。
註:Log4j 在輸出日誌訊息時,通常是以附加的方式(例如:附加到檔尾),因此用來輸出的類別,就叫做附加器(appender)。
| 注意在建置 Web 專案時,編譯 Java 原始檔所產生的 .classes 檔會輸出到 WEB-INFclass 目錄下,而有些開發工具,例如:Eclipse,在建置 Web 專案時會先清空 WEB-INFclasses 目錄,並且自動將 Java 原始碼目錄下的非 .java 檔案複製一份到 WEB-INFclasses 目錄下,因此前面提到的 commons-logging.properties 和 og4j.properties 屬性檔就要放在 Java 原始碼目錄下。 |
3.2 撰寫程式
若要在 JSP 程式中輸出日誌訊息,主要有三個步驟:
- 引入(import)兩個必要的類別:Log 和 LogFactory。
- 透過 LogFactory 取得 log 物件。
- 使用 log 物件輸出日誌訊息。
以下是一個簡單的 JSP 範例程式:
<HTML> <HEAD> <%@ page language="java" contentType="text/html; charset=BIG5" pageEncoding="BIG5" import="org.apache.commons.logging.Log" import="org.apache.commons.logging.LogFactory" %> <% Log logger = LogFactory.getLog(this.getClass()); %> <META http-equiv="Content-Type" content="text/html; charset=BIG5"> <META name="GENERATOR" content="IBM WebSphere Studio"> <TITLE>TestLog4j.jsp</TITLE> </HEAD> <BODY> <% logger.info(logger.getClass().getName()); logger.debug("Debug message."); logger.info("Info message."); logger.warn("Warn message."); logger.error("Error message."); %> </BODY> </HTML>
執行這個範例程式,你應該會在 console 視窗或者你的應用程式伺服器的 log 檔裡面看到像這樣的訊息:
INFO [Servlet.Engine.Transports : 0] (_TestLog4j.java:77) - org.apache.commons.logging.impl.Log4JLogger INFO [Servlet.Engine.Transports : 0] (_TestLog4j.java:81) - Info message. WARN [Servlet.Engine.Transports : 0] (_TestLog4j.java:82) - Warn message. ERROR [Servlet.Engine.Transports : 0] (_TestLog4j.java:83) - Error message.
其中第一個訊息是用來顯示目前使用的 logging 實作類別。
這個範例程式很簡單,但是有幾個地方值得注意一下:
- 在程式裡我們呼叫了輸出了五個日誌訊息,但是其中 logger.debug() 的訊息並沒有輸出。這是因為在前面的 log4j.properties 屬性檔中,我們指定了日誌輸出的層級是 INFO(log4j.rootLogger=INFO),而 DEBUG 訊息的層級比 INFO 更高,因此 log4j 不會輸出 DEBUG 訊息。Log4j 日誌訊息的層級有下列幾個(從高到低排列):
- TRACE(最高層級)
- DEBUG
- INFO
- WARN
- ERROR
- FATAL(最低層級,最嚴重的錯誤才會輸出到 log)
在程式中輸出 log 訊息時,應該根據該訊息的嚴重程度來呼叫對應的輸出函式,例如:非常嚴重的錯誤,可使用 fatal(),這樣的話,以後可以隨時視需要調整 log4j.properties 屬性檔的 log 層級,來過濾掉一些不重要的訊息 ,同時也可以提升程式執行的效能。
註:早期版本還有 ALL 跟 OFF,但已經廢棄不用,應避免使用它們。因為最高層級 TRACE 跟 ALL 的作用是一樣的;如果你想關閉日誌功能,只要在屬性檔中不指定 appender 就行了。
- 我們在 JSP 網頁中引入的類別是 Jakarta Commons Logging 的類別,可是實際上卻是透過 log4j 來執行輸出訊息的工作,這就是在第 1 節裡面提到的,使用 Jakarta Commons Logging 的好處:它讓你不用綁死在特定的 logging 實作產品上。當你想要更換成別的 logging 元件,原有的程式都不用修改,只要更改設定檔就行了。
如果要在一般 Java 應用程式輸出 log 訊息,基本的步驟也跟前面描述的差不多。
3.3. 一些實務建議
在程式中使用 logging 時,比較需要注意的地方主要是跟效能有關,以下是一些使用 logging 的建議。
3.3.1. 選擇正確的日誌層級
你應該根據輸出訊息的嚴重程度,呼叫適當的輸出方法,例如:Log.debug()、Log.info()、Log.error()….等,這樣的話,以後只要調整屬性檔就可以控制哪些訊息要輸出,有助於篩選及尋找特定的訊息。至於怎樣區分嚴重的程度,並沒有一個通用的規則,主要還是由程式設計師自行判斷。
3.3.2 使用 StringBuffer 取代 String
要輸出的 log 訊息有時候必須串接多個字串,此時最好採用 StringBuffer,以提升一點效能。例如:
String s := "登入失敗 :" + ex.getMessage() + " - 使用者 ID: " + userID + ", 密碼: " + password); log.error(s);
可以改成:
StringBuffer sb = new StringBuffer(); sb.append("登入失敗:"); sb.append(ex.getMessage()); sb.append(" - 使用者 ID: "); sb.append(userID); sb.append(", 密碼: "); sb.append(password); log.error(sb.toString());
3.3.3. 輸出 log 之前先檢查日誌層級是否有效
在某些場合,輸出 log 訊息之前,可以先檢查某種 logging 層級是否有效,以改善應用程式的效能。例如,你有一段要輸出的 log 訊息,前面的程式碼可以改成:
if (log.isDebugEnabled()) {
StringBuffer sb = new StringBuffer(); sb.append("登入失敗:"); sb.append(ex.getMessage()); sb.append(" - 使用者 ID: "); sb.append(userID); sb.append(", 密碼: "); sb.append(password); log.error(sb.toString()); }
這樣的話,當 log4j 的屬性檔設定的層級低於 DEBUG 時,整段組成 StringBuffer 的程式碼就不必執行,可以節省一些時間。如果你需要在迴圈裡面輸出 log 訊息,此技巧能節省的時間就更可觀了。
3.3.4. 注意同步的問題
如果有多個執行緒使用相同的 appender 來輸出訊息,Log4j 不會有問題,因為所有執行緒都是同步化的(synchronized)。但是如果你有好幾個不同的 appender 要同時寫入訊息到同一個檔案或輸出裝置,就可能會發生無法預期的結果。
3.3.5. 選擇適當的 appender
Log4j 提供了多種 appender,除了 Console Appender、File Appender 之外,還有 Socket Appender、SMTP appender、NT Event Logger Appender….等等,你可以參考 log4j 的說明文件以了解這些 appender 的使用方式。以下提供一個 log4j.properties 屬性檔範例,這個屬性檔使用了兩個 log4j 的附加器:ConsoleAppender 以及 RollingFileAppender。
log4j.rootLogger=INFO, stdout, logfile log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n log4j.appender.logfile=org.apache.log4j.RollingFileAppender log4j.appender.logfile.File=c:/myweb.log # 每個 log 檔最大 size 限制 log4j.appender.logfile.MaxFileSize=512KB # 保留幾個備份檔 log4j.appender.logfile.MaxBackupIndex=3 log4j.appender.logfile.layout=org.apache.log4j.PatternLayout # 輸出型樣: date priority [category] - <message>line_separator log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - <%m>%n
其中 RollingFileAppender 用來將日誌訊息輸出到指定的檔案,並且可限定檔案大小的限制,當日誌檔大小出過這個限制,就將檔案備份出去,你還可以指定備份檔最多要有幾個,超過時會循環使用。此範例指定日誌檔案為 c:/myweb.log,如果 myweb.log 超過 512KB,就備份出去,且備份檔最多為 3 個,備份檔會以 myweb.long.# 的方式命名,其中 # 是備份檔的編號,例如:myweb.log.1。
此外,log4j 還有另外一個能夠自動備份與翻新(rollover)日誌檔的 appender:org.apache.log4j.DailyRollingFileAppender,它除了能夠被備份日誌檔,也可以根據使用者設定的期間翻新日誌檔,避免日誌檔毫無節制的成長。
4. JDK 的 Logger 類別
JDK 從 1.4 版開始也提供了 logging 元件,提供這項功能的類別是 java.util.logging.Logger。這裡也附上一個簡單的使用範例:
/* * TestLogging.java * * Requires JDK 1.4 */ import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import java.util.logging.FileHandler; public class TestLogging { private static Logger logger = Logger.getLogger(TestLogging.class.getName()); public TestLogging() { try { FileHandler fileHandler1 = new FileHandler("log.txt"); logger.addHandler(fileHandler1); } catch(IOException ioe) { logger.log(Level.SEVERE, "Unable to create file handler.", ioe); } logger.log(Level.SEVERE, "This is a severe level message!"); logger.log(Level.SEVERE, "This is a second severe level message!"); } public void testLog() { logger.info("Message sent from method call."); } public static void main(String[] args) throws java.io.IOException { TestLogging example = new TestLogging(); example.testLog(); } }
執行此範例程式時,可以在畫面看到 log 訊息,而且訊息也會以 XML 的格式寫入 log.txt 檔案裡面。
你可以跟 log4j 的使用方式比較一下,兩者在使用上的概念其實是差不多的,只是 log4j 功能更豐富,而且 JDK logging 只有 1.4 版以後才有,而 log4j 可支援比較早期的 JDK 1.3,對某些人來說,這可能是蠻重要的考量。
5. 總結
在開發應用程式時,logging 機制除了讓你將事件記錄下來供日後核對,對應用程式的除錯也很有幫助。本文簡短的說明了如何將 logging 機制 加入 Web 應用程式,一些 log4j 的進階功能並沒有提到,主要是希望從來沒接觸過 log4j 的人,能夠透過本文盡快上手,將 logging 機制加入自己的程式裡。如您所見,Jakarta Commons Logging 搭配 log4j 的方式是非常簡單易用的,除了功能強大之外,它還有一個最大的優點,就是只要調整屬性檔的設定就能控制日誌檔的輸出方式,不需要老是去修改程式。當基本的功能大致了解後,可參考相關的書籍或 網站上的說明文件,以了解其他進階的應用,http://logging.apache.org/log4j/docs/download.html 裡面也有許多輔助的套件或工具,也建議您花點時間看看。
參考資料
| 1 | http://jakarta.apache.org/log4j/docs/ |
| 2 | Commons Logging API http://jakarta.apache.org/commons/logging/api/index.html |
Detect IE version
var is_ie/*@cc_on = {
// quirksmode : (document.compatMode==”BackCompat”),
version : parseFloat(navigator.appVersion.match(/MSIE (.+?);/)[1])
}@*/;
(Uncomment the second line if you also wish to check whether IE is running in “compatibility” (quirks) mode or in standards-mode.)
All other browsers cheerfully ignore the Javascript comment block (/* … */) so what they end up with is essentially this:
var is_ie;
…which evaluates as an implicit false, when used as a condition. This allows us to set up conditions like this one:
if (is_ie && (is_ie.version < 7))
{
// do IE specific stuff
}
else if (is_ie && (is_ie.version >= 7))
{
//do IE 7 or above specific stuff
}
else
{
// default behavior for other browsers
}
[reference : http://mar.anomy.net/entry/2006/11/23/00.06.40/ ]
DWA: IE7 fixes for Domino 6.5.5
This document contains instructions for obtaining a fix to address Daylight Saving Time 2007 changes and IE7 issues with Domino Web Access (DWA) 6.5.5.
DJAG6TZMNT: IE7 closes with reuse child windows and ‘open pop-ups in new tab’ setting
VSEN6MH3YD: IE7…resizing columns in DWA views yield JavaScript error
LLGO6UW3E3: IE7 only: DWA warning pops up when send an “Accept with Comment”
LCDL6UWB7J: IE7 only: DWA warning pops up when invitee send an “accept with comments” for meeting type
iText 經驗總結
iText經驗總結
因為前些日子在一個項目中用到了iText,稍有收獲,便總結于此,以供他人所需。
iText是一個比較底層的pdf庫,很多項目的pdf操作都是以它為基礎的。像spring,以及另一個比較有名的報表工具jasperreports。簡單的pdf報表輸出用它比較合適,比較復雜的話使用起來就比較困難了,你要手工編寫太多的代碼。
比較好的是iText網站上提供相當多的示例代碼,比較容易入門。我這里只說一些在它的文檔里並沒有直接講到的東西。
1 關于Document
Document的几種构造函數:
public Document();
public Document(Rectangle pageSize);
public Document(Rectangle pageSize,
int marginLeft,
int marginRight,
int marginTop,
int marginBottom);
下面兩種比較有用,如果是你想定義紙張大小和邊緣的時候。對于Margin,iText上提到”You can also change the margins while you are adding content. Note that the changes will only be noticed on the NEXT page. If you want the margins mirrored (odd and even pages), you can do this with this method: setMarginMirroring(true). “不過,對于table似乎並不好使。table並不會了理會你設定的margin,如果想改變它的magin還是需要去改變它的寬度(setWidth)。
2 pdf表單
使用PdfStamper是可以填充pdf表單的,這樣就給出了一種很好的報表生成思路。
word制作報表樣式–>acrobat轉pdf–>itext填充數据–>輸出pdf
這做非常簡單,因為可以比較容易的控制pdf的樣式。我對于Java的報表工具了解的並不多,不過在jasperreports,即使用GUI工具做一個樣式比較復雜的報表也不是怎麼容易。比如有那種斜線的表頭,比較花哨的嵌套表格。這樣的情況還是比較多見的,客戶不會關系你實現起來是否困難。不過想要使用這種方式也有不足的地方。首先是acrobat把word轉化成pdf的時候,格式總是保持不好,特別的是字體。然后是文件的體積這樣生成的pdf會比直接用iText生成的pdf文件大很多,acrobat在pdf里加入了太多無用的信息。初次使用iText填充Adobe Designer生成的pdf表單時會有點小麻煩。在Designer中設計了一個name的text文本框的綁定名為name。照著iText中例子使用使用PdfStamper的setField方法去這樣寫form.setField(“name”, “XXXX”);並不會成功。原因是Adobe Designer生成的表單名都是具有層次的,它可能是這個樣子form1[0].#subform[0].name[0]。不過我們可以用一個方法把它們列出來,只要做一次就知道結构了,可以使用類似下面的代碼:
PdfReader reader = new PdfReader(“form.pdf”);
PdfStamper stamp = new PdfStamper(reader, new FileOutputStream(“registered_flat.pdf”));
AcroFields form = stamp.getAcroFields();
for (Iterator it = form.getFields().keySet().iterator(); it
.hasNext();) {
System.out.println(it.next());
}
如果直接用iText編程生成的表單就不會有這樣的問題,設定的什麼名字就是什麼名字。
3 表單元素
pdf並不像html那樣具有良好清晰的結构,而是一個有層次的文檔類型。在它的maillist里,作者說明了iText雖然可以操作現存的pdf文件但是沒辦法去還原它的結构的。沒辦法像html一樣,能從一個pdf文件獲得一個清晰的”源文件”的。關于層次,可以從iText上得到詳細的講述,獲取去看看pdf規范。表單和普通文本是不在一個層上的。沒辦法適用對待文本表各一樣把它們簡單的add進Document對象。獲取一個cb直接去用絕對定位的方法可以加入表單元素,不過很多的時候因為排版並不能那麼簡單的去做。就是在html中布局一樣可以使用表格定位。想把一個表單元素加入cell,要借助cell的setCellEvent方法。以一個checkbox為例。新建一個類CheckBoxForm,實現PdfPCellEvent接口。需要實現一個cellLayout的方法。
public void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases)
position可以好好利用,它包含當前cell的位置信息,你可以用它來确定自己checkbox的位置。
position.top()-position.bottom()就能得到高position.right()-position.left()可以得到長,如果需要這兩個值得花可以如此計算。下面的代碼就是定義一個寬度為a的checkbox的rectangle 。它在cell中水平居中,垂直也居中。
float bo = (position.top()-position.bottom()-a)/2;
float ao = (position.right()-position.left()-a)/2;
Rectangle rectangle = new Rectangle(position.left() + ao, position
.bottom() + bo, position.left() +ao+ a, position.bottom()+ bo + a);
然后把它加入Document
RadioCheckField tf = new RadioCheckField(writer, rectangle, fieldname,
“f”);
tf.setCheckType(RadioCheckField.TYPE_SQUARE);
tf.setBorderWidth(1);
tf.setBorderColor(Color.black);
tf.setBackgroundColor(Color.white);
try {
PdfFormField field = tf.getCheckField();
writer.addAnnotation(field);
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
其它的元素與此類似。
4 PdfPTable和Table
說不上哪種更好用,有時候不能不使用PdfPTable。可惜它只有setColspan方法,沒有setRowspan。嵌套的時候也有區別,PdfPTable是用addcell()加入嵌套表的,table則有一個更明了的方法insertTable()。PdfPTable想進行設置border之類的操作要先獲得一個默認cell,
pdfPTableName.getDefaultCell().setBorder(Rectangle.NO_BORDER);//設置無框的表
另外在PdfPTable中,一些修飾屬性會因為設置的時机不正确而沒有效果。如,適用cell的构造函數加入了文本,在cell的setVerticalAlignment()fangfa去設定垂直對齊方式就不會有效。還有一個有意思的不同是table默認外邊框是加粗的,而PdfPTable則一樣粗細。
5 字體
iText的例子有很多足夠用,給出一些pdf的字體名稱和編碼,如果想使用內嵌字體的話。
語言 PDF 字體名
簡體中文 STSong-Light
繁體中文 MHei-Medium
MSung-Light
日語 HeiseiKakuGo-W5
HeiseiMin-W3
韓語 HYGoThic-Medium
HYSMyeongJo-Medium
字符集 編碼
簡體中文 UniGB-UCS2-H
UniGB-UCS2-V
繁體中文 UniCNS-UCS2-H
UniCNS-UCS2-V
日語 UniJIS-UCS2-H
UniJIS-UCS2-V
UniJIS-UCS2-HW-H
UniJIS-UCS2-HW-V
韓語 UniKS-UCS2-H
UniKS-UCS2-H
必須要有Asian的包才可以用,也可以使用TrueType字體。
ps:因為隔了一段時間了,所以有些現在一時也想不起來了,也可能會有理解的錯誤。另外,適用iText的時候自己最好抽象一下,可能會省不少力氣。
Sysinternals: AutoRuns, TCPView, Process Explorer
http://www.microsoft.com/technet/sysinternals/default.mspx
今日無意中找到, autoruns 俾msconfig 好好多~~~ sysinternals 都係近期先俾m$ 收購左~~
AutoRuns for Windows v8.61
http://www.microsoft.com/technet/sysinternals/Security/Autoruns.mspx
執行 Windows® 的一般個人電腦會在開機時載入各種項目,包括檔案、驅動程式、工作及服務。有時候,您很可能需要檢視或停用某些啟動項目,以便疑難排解啟動衝突、依照自動啟動順序追蹤惡意程式碼、縮短 Windows 啟動時間,或是釋放記憶體和系統資源。
系統設定公用程式 (msconfig.exe) 可讓您檢視和停用一些啟動檔案和服務,但還是有不少它無法管理的項目:工具列、瀏覽器協助程式物件、Windows Explorer 殼層延伸。而且,msconfig 無法提供關於這些項目的詳細資訊。若要尋找更好的方法來檢視及管理啟動時載入的所有項目,不妨考慮 Sysinternals 推出的 Autoruns。
HylaFax
1. Download Binary file
http://www.hylafax.org/content/Handbook:Binary_Package_Install
2. Install
Install the HylaFAX RPM file(s) via ‘rpm -Uvh hylafax*rpm’
Run /usr/sbin/faxsetup and then /usr/sbin/faxaddmodem and answer the questions appropriately
using default setting and answer the setup script
(stupid, forcing to enter area code …. HKSAR have none….)
Country code [1]? 852
Area code []? 0
Long distance dialing prefix [1]? 0
International dialing prefix [011]? 001
Serial port that modem is connected to []? ttyS0
Modem in COM1 => /dev/ttyS0
COM2 => /dev/ttyS1
Local Identifications string (for TS/CIG) ["NothingEtup"]? “Company Name” (Display on Receiver Fax Machine)
3. config
Add the line: ‘mo:2345:respawn:/usr/sbin/faxgetty ttyS0′ to /etc/inittab
#/usr/sbin/ntsysv and make sure that the hylafax service is set to be started
#vi /etc/aliases and change the appropriate line to ‘FaxMaster: faxuser@mydomain.com’
where faxuser@mydomain.com is the intended e-mail address of the incoming fax recipient
#init q
4. Add user
# faxadduser username
# faxadduser -p password username
- specify which client machines and users can have access to a HylaFAX? server machine.
modify etc/hosts.hfaxd, add client IP
5. status
faxstat Query /var/spool/hylafax/status/ttyS0
faxstat -r Query /var/spool/hylafax/recvq received fax
faxstat -s Query /var/spool/hylafax/sendq sending fax
faxstat -d Query /var/spool/hylafax/doneq sent fax
xferfaxstats Analysis report from /var/spool/hylafax/etc/xferfaxlog
faxrm remove “waiting to send” fax
6. Reference:
http://csc.ocean-pioneer.com/docum/csc_hylafax.html
http://csc.ocean-pioneer.com/docum/hylafax_client.html
character set conversion
java, domino, embeddedobject
String strEOname = new String(eo.getName().getBytes(“Big5″), “8859_1″);

