2007年9月28日 星期五

.NET 2.0 中 Crystal Report 到達處理上限的解決方式

今天系統的報表出現了一些問題,其中一個最重要的訊息如下:
已經到達您系統管理員所設定的最大報表處理工作限制
後來找到解決方式,只要修改登錄檔中「PrintJobLimit」的上限值,再重新啟動 IIS 即可!

參考連結
  1. CrystalReport 已經到達您系統管理員所設定的最大報表處理工作限制
  2. Error: "System.Exception: Load report failed…” when a web application processes a large print job

2007年9月25日 星期二

Java 學習筆記 (10) - Reflection

class 的載入與檢視

class 載入

為了妥善使用有限的資源,Java 在真正需要使用到 class 的時候才會將其載入,當每一個 class 被載入時,JVM 就會為其自動產生一個 Class object。

【註】每一個 class 載入僅會有一個 Class object。因此即使使用同一個 class 產生多次 object,也只會有一個與其對應 Class object。

每一個 object 都可以透過 getClass() 的方式取得 Class object,以下為一個簡單的範例:
public class ClassDemo {
public static void main(String args[]) {
String name = "godleon";
Class stringClass = name.getClass();

System.out.println("類別名稱:" + stringClass.getName());
System.out.println("是否為介面:" + stringClass.isInterface());
System.out.println("是否為基本型態:" + stringClass.isPrimitive());
System.out.println("是否為陣列物件:" + stringClass.isArray());
System.out.println("父類別名稱:" + stringClass.getSuperclass().getName());
}
}

而 Java 只有在真正要用到 class 的時候才會將其載入,而真正用到的時候是指以下情況:
  1. 使用 class 生成 object 時
  2. 使用者指定要載入 class (利用 Class.forName() 或是 ClassLoader.loadClass())

若僅是宣告並不會載入 class,以下用一段程式碼來測試:

TestClass.java
public class TestClass {
static {
System.out.println("類別被載入");
}
}
LoadClassTest.java
public class LoadClassTest {
public static void main(String args[]) {
TestClass test = null; //class不會載入,因此不會顯示「類別被載入」
System.out.println("宣告 TestClass 參考名稱");
test = new TestClass(); //class被載入,顯示「類別被載入」
System.out.println("生成 TestClass 實例");
}
}

此外,由於 Java 支援 Run-Time Type Identification(RTTI,執行時期型別辨識),因此 Class 的訊息在編譯時期就會被加入 .class 檔案中;而執行時,JVM 則會在使用某 class 時,會先檢查相對應的 Class object 是否已經載入,如果沒有載入,則會尋找相對應的 .class 檔案載入。

而一個 class 在 JVM 中只會有一個 Class object,所以每個 object 都可以透過 getClass() 或是 .class 屬性取得 Class object。

之前有說過,在 Java 中任何東西皆為 object,因此 Array 也不例外,甚至如 primitive type、關鍵字 void 都有相對應的 Class object,以下用一段程式來說明:
public class ClassDemo2 {
public static void main(String args[]) {
System.out.println(boolean.class); //boolean
System.out.println(void.class); //void

int[] intAry = new int[10];
System.out.println(intAry.getClass().toString()); //class [I

double[] dblAry = new double[10];
System.out.println(dblAry.getClass().toString()); //class [D
}
}
Class 類別中的 forNamenewInstance 方法

若要動態載入類別,可以使用 Class 類別中的 forName() method,此為 static method,因此可以直接透過 Class 類別使用;另外,若要取得類別的 instance,可使用 Class 類別中的 newInstance() method,以下用依段程式來說明兩個 method 的使用方法:
import java.util.*;

public class ClassForNameDemo {
public static void main(String[] args) {
try {
//forName的使用
Class c = Class.forName(args[0]);
System.out.println("getName : " + c.getName());
System.out.println("isInterface : " + c.isInterface());
System.out.println("isPrimitive : " + c.isPrimitive());
System.out.println("isArray : " + c.isArray());
System.out.println("SuperClass : " + c.getCanonicalName());

//newInstance的使用
List lst = (List) c.newInstance();
for(int i = 0 ; i < 10 ; i++)
lst.add("element" + i);
for(Object o : lst.toArray())
System.out.println(o);
}
catch(Exception e) {
e.printStackTrace();
}
}
}
而當使用 newInstance() 取得 instance 後,就可以使用 getClassLoader()getConstructor()getFields()getMethods()getModifiers()getPackage() ..... 等方法,取得 Class 所產生的 instance 的相關資訊,而類別檢視器就是利用這樣子的方式所做出來的!


Class Loader

當 Java 需要使用到 Class 時,才會將 Class 載入;而載入 Class 的工作是由 Class Loader(類別載入器) 所負責。

每當 Java 程式啟動後,會有以下三種 Class Loader 進行載入 Class 的工作:
  1. Bootstrap Loader
    由 C++ 開發,會去搜尋 JRE 目錄($JAVA_HOME/jre/)中的 class 以及 lib 資料夾中的 *.jar 檔,檢查是否有指定的 class 需要載入。
  2. ExtClassLoader
    會去搜尋 JRE 目錄($JAVA_HOME/jre/)中的 lib/ext 資料夾,檢查其中的 class 與 *.jat 檔案,是否有指定的 class 需要載入。
  3. AppClassLoader
    搜尋 classpath 中是否有指定的 class 需要載入。
以下用一張圖形來說明三個 Class Loader 的階層關係:

其中 Bootstrap Loader 為 ExtClassLoader 的 Parent Class;而 ExtClassLoader 則是 AppClassLoader 的 Parent Class。

而從 JVM 啟動後,載入的順序為 Bootstrap Loader -> ExtClassLoader -> AppClassLoader。

而每個 class 所產生出來的 instance 都可以使用 getClassLoader() 知道負責載入 class 的是哪一個 class loader。


動態載入類別、產生物件、呼叫方法

接著在這個部分,要介紹的是如何動態的載入將已經寫好的 class,並產生物件來呼叫 class 中所包含的 method,以下用兩段程式說明:

Student.java
public class Student {
private String name;
private int score;

public Student() {
name = "N/A";
}
public Student(String name, int score) {
this.name = name;
this.score = score;
}

public void setName(String name) {
this.name = name;
}
public void setScore(int score) {
this.score = score;
}

public String getName() {
return name;
}
public int getScore() {
return score;
}

public void showData() {
System.out.println("Name : " + this.name);
System.out.println("Score : " + this.score);
}
}
newInstanceDemo.java
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class newInstanceDemo {
public static void main(String[] args) {
Class c = null;
try {
c = Class.forName(args[0]);

/* (Begin)=============== 生成物件 ===============(Begin) */

//指定Constructor所使用的參數型態
Class[] oParam = new Class[2];
oParam[0] = String.class;
oParam[1] = Integer.TYPE;

//產生Constructor
Constructor constructor = c.getConstructor(oParam);

//指定參數的內容
Object[] paramObjs = new Object[2];
paramObjs[0] = "godleon";
paramObjs[1] = new Integer(90);

//透過Constructor產生物件
Object obj = constructor.newInstance(paramObjs);
System.out.println(obj);

/* (End)=============== 生成物件 ===============(End) */


/* (Begin)=============== 呼叫方法 ===============(Begin) */

//指定Method所使用的參數類型
Class[] mParam1 = {String.class}; //只有一個參數

//產生Method(指定method名稱與參數)
Method setName = c.getMethod("setName", mParam1);

//指定參數內容
Object[] mParamObjs1 = {"godleon"};

//呼叫方法
setName.invoke(obj, mParamObjs1); //呼叫setName方法

//指定Method所使用的參數類型
Class[] mParam2 = {Integer.TYPE};

//產生Method(指定method名稱與參數)
Method setScore = c.getMethod("setScore", mParam2);

//指定參數內容
Object[] mParamObjs2 = {new Integer(90)};

//呼叫方法
setScore.invoke(obj, mParamObjs2); //呼叫setScore方法

//產生Method(指定method名稱與參數)
Method showData = c.getMethod("showData", null);

//呼叫方法
showData.invoke(obj, null); //呼叫showData方法

/* (End)=============== 呼叫方法 ===============(End) */
}
catch(Exception e) {
e.printStackTrace();
}
}
}
由上面的程式碼可以知道,若是有執行時期動態載入 class,並呼叫其 method 的需求,透過使用 reflection,可以很簡單達成目的。

另外也可以動態的修改 Field 的值,可透過 getField() method 來達成,使用方式跟上面的程式大同小異,而若是要瞭解詳細的使用方式,可以參考此篇文章

最後,此篇筆記就是著重在瞭解 Java 如何在執行期間動態載入 class 並使用,未來有機會用到的時候再來繼續深入探討.....

2007年9月20日 星期四

.NET DateTime 與 Unix Timestamp

剛剛在研究如何將 Unix Timestamp 與 .NET 中的 DateTime 物件互轉!

找到了以下幾篇文章:
  1. 瓶水相逢 - C# - Timestamp 與 DateTime 互轉
  2. UNIX timestamp to System.DateTime

然後還需要跟 JavaScript Date 物件中的 getTime() 方法進行轉換,然後用了以下程式碼:(忘記從哪一篇找來的.....)
//得到1970年的 timestamp
DateTime timeStamp = new DateTime(1970, 1, 1);

//注意這裡有時區問題,用now就要減掉8個小時
DateTime realNow = (new DateTime(2007, 9, 20, 12, 0, 0)).AddHours(-8);

//jsTimeStick即等於用JavaScript Date物件的getTime()方法跑出來的值
long jsTimeStick = (realNow.Ticks - timeStamp.Ticks) / 10000;

2007年9月17日 星期一

Windows Scripting - 簡易資料備份

今天收到命令,要做定時的備份工作,備份的目的地要在遠端的網路磁碟機上

掛載、卸載網路磁碟機

因此就去查詢如何在 Windows 上寫 script 掛載網路磁碟機,以下找到一段語法:
WshNetwork.MapNetworkDrive
maps a remote drive onto a local drive letter

Syntax:
WshNetwork.MapNetworkDrive (strLocalName, strRemoteName [,bUpdateProfile] [,strUser] [,strPassword])
strLocalName
Receives the drive letter to which to map the remtoe share point in strRemoteName.
strRemoteName
Receives the name ofd the remote share point that will be mapped to the drive letter in strLocalName.
bUpdateProfile
If this optional parameter is True, the mapping will be saved in the current user's profile.
strUser
When combined with strPassword, this parameter can be used to conenct to a remote drive using someone else's credentials.
strPassword
When combined with strUser, this parameter can be used to conenct to a remote drive using someone else's credentials.
接著是卸載網路磁碟機的語法:
WshNetwork.RemoveNetworkDrive
removes a previously mapped network drive

Syntax:
WshNetwork.RemoveNetworkDrive (strName, [,bForce] [,bUpdateProfile])
strName
Receives the name of a previously mapped share point to be removed.
bForce
If this optional parameter is set to True, then this method will remove the connection even if the resource is currently being used.
bUpdateProfile
If this optional parameter is set to True, the mapping will be removed from the current user's profile.

以下是他的 sample code:
Dim objNetwork
Set objNetwork = WScript.CreateObject("WScript.Network")
strLocalDrive = "H:"
strRemoteShare = "\\myserver\users"
objNetwork.MapNetworkDrive strLocalDrive, strRemoteShare, False '沒有第四、五個參數,表示不需帳號密碼
objNetwork.RemoveNetworkDrive strLocalDrive '卸載網路磁碟機

資料複製

另外還要將本地資料複製到網路磁碟機上,可用以下一段簡單的語法達成:(也是範例語法)
set fs = CreateObject("Scripting.FileSystemObject")

'將 c:\win98 中所有的內容複製到 z:\backup (不包含 c:\win98 資料夾)
fs.copyFolder "c:\win98","z:\win99"

'將 c:\win98 中所有的內容複製到 z:\backup (包含 c:\win98 資料夾)
fs.copyFolder "c:\win98","z:\win99\"

參考資料

  1. WSH >> Objects >> wshnetwork
  2. WSH >> wshnetwork >> MapNetworkDrive
  3. WSH >> wshnetwork >> RemoveNetworkDrive
  4. WshNetwork.MapNetworkDrive
  5. 資安論壇 - 求助!有時需要reboot或re-log才看見net use的network drive
  6. Windows Script 介紹 - 檔案及資料夾的處理

在 Ubuntu 中建置 mono 開發環境

在 Ubuntu 中建置 mono 的開發環境相當簡單,需要以下三個要素:
  1. mono (Runtime)
  2. mono-gmcs (compiler)
  3. monodevelop (IDE develop tool)
所以下達以下指令通通解決:
shell> sudo apt-get -y install mono monodevelop mono-gmcs
接著就可以使用 monodevelop 來開發 .NET 程式囉!

2007年9月6日 星期四

SAMBA + FTP 在 SELinux 的調整方式

要針對同一個目錄,讓可以透過 SAMBA 連入! 也可讓 FTP 連入!

在 SELinux 開啟的情況下,要將該目錄的 security context 設定為 「samba_share_t

如此一來當權限設定好之後,就可以正常的透過服務存取目錄的資料了!

2007年9月4日 星期二

Linux SATA HowTo

關於 Linux 對於 SATA chipset 的支援,隨著 kernel 版本的持續更新,以及社群的強大協助開發下,陸陸續續將許多廠商所生產的 SATA chipset driver 都已經加入,因此在目前比較新的 Linux distribution 如 Fedora 7Ubuntu 7.04OpenSUSE 10.2Mandriva Free 2007.....等,對於 SATA 的支援度都已經相當的好。

此外,若想要清楚了解目前 Linux 已經支援了哪些 SATA chipset,可以參考以下兩個網站,資料整理的相當詳細:

因此在購買硬體前,事先進行確認,可以避免掉後續許多硬體不支援的麻煩。

但若是因為一時不察,買到了內建目前各大 Linux distribution 都不支援的 SATA chiset 的產品(例如:主機板、SATA RAID Card .... 等等),雖然麻煩了點,但還是可以透過幾個方式解決:

1、進入 BIOS 設定為相容模式
在 BIOS 中尋找 SATA 的設定,假設在 IBM X60 上,可以找到 Config -> Serial ATA(SATA),設定為 Compatibility。

在其他不同廠牌的 BIOS 中,可能會有 Legacy ATA Mode 可供設定!

透過此種方式,BIOS 會將 SATA Device 視為 PATA 處理,當然效能會有所折損,不過可以在順利安裝 Linux 後,可以透過以下方式讓 Linux 支援 SATA 的裝置:
  1. 透過線上更新將 kernel 換成最新版的
    在 Fedora 中可以使用 yum;在 Ubuntu 中可以使用 apt。kernel 更新完成後,可能就支援原本不支援的 SATA 了,但也有可能一樣不支援!
  2. 重新編譯 kernel
    若是在 kernel 官方網站中,發現其實在新版的 kernel 中已經支援了該 SATA 裝置,就可以下載 kernel 的 source code 後,開啟 SATA 的支援並重新進行編譯,如此一來就可以正確驅動該 SATA chipset,接著置換原本的 kernel,就可以解決 SATA 裝置無法驅動的問題。而編譯 kernel 的方式可以參考此篇文章(鳥哥的 Linux 私房菜 - 核心編譯)


2、使用廠商提供的 SATA chipset driver

若是很清楚 SATA chipset 是哪家廠商所生產的,並找到該廠商所提供的官方版 driver,就可以在安裝時,使用「linux dd」指令在安裝前載入 SATA chipset driver,如此一來安裝時,就可以正確的驅動 SATA device。

PS. 其實此種解決方式跟在 Windows 的模式是差不多的,在安裝 Windows 前若要載入 SATA driver,必須按 F6;只是在 Linux 的安裝中,必須下達指令「linux dd」。


結論
假設廠商沒有提供任何的 driver,線上更新 kernel 後也還是不支援,甚至在 Linux kernel 官方網站中都沒有提供支援該 SATA chipset 的計劃,就表示目前該 SATA chipset 在 Linux 是無法正常運作的,因此可能要重新購買其他已經支援的產品。


參考資訊

2007年9月3日 星期一

SQL Server 學習筆記 (1)

今天 K SQL server 的書,學到了幾個新的小技巧,因此作個記錄~

指令檢視 Table 詳細資訊

透過以下指令可以檢視 Table 的詳細資訊:
--指定 Database
USE studentPartTime
go
--查詢 Table 資訊
sp_help 'tbpwWork'
如此一來,table 的 schema、key、relation 都會很詳實的呈現出來,若是有 Date 相關欄位,還會有相對應的資訊顯示出來。


Index

除了 schema 設計的好壞之外,Index 的建置對於資料庫 performance tunning 是相當重要的一環,以下找到幾篇文章說明 index 設計時所必須考量的因素:
這幾篇看完應該會很清楚 index 應該要怎麼用了!

之後研究有其他更深入的心得,就再貼上來......


Set-Oriented (資料集導向)

在 RDBMS 中,最佳化都是針對 Data Set (資料集),若是用程序式的方式進行資料的操作,所得到的效能反而不好。

假設有個範例,要搜尋出住在各縣市的學生人數的統計資料!

若是用程序式的處理方式,就必須使用 cursor,並透過變數暫存數值,一筆一筆資料從頭掃瞄到尾,以便統計出完整資訊。

而用 Data Set 的處理方式,則是利用 select、count、group by 等關鍵字即可完成,遠遠比起使用 cursor 的方式來的輕鬆有效率,因此在 DML 的部分還是要多加鑽研才行!