2011/08/29

OpenEJB for Tomcat

其實本來專案中的 EJB 是要部署在其他 container 上的,只是懶的重新建立 server,於是找了一下有沒有可以讓 Tomcat 可以支援 EJB 的東西,沒想到還真的有呢!於是簡單的紀錄一下測試的步驟。

環境設定
基本需求:
快速安裝步驟:
  1. 首先你要有一個能正常運行的 Tomcat server
  2. 把從 OpenEJB 抓下來的 openejb.war 放到 ${tomcat.home}/webapps/ 下
    (注意:這個檔案必須名為openejb.war,所以不要去改名稱...)
  3. 啟動 Tomcat,打開網址 http://localhost:8080/openejb/installer 並點下 Install 按鈕

EJB 程式碼部分
由於是舊系統,所以這邊用的是 EJB 2.x 的規格來寫,範例寫的是一顆 stateless 的 session bean,EJB 的介紹在這邊就不詳述了。

Remote介面
import java.rmi.RemoteException;
import javax.ejb.EJBObject;

public interface FruitShop extends EJBObject {
    public String getBanana() throws RemoteException;
}
Home介面
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;

public interface FruitShopHome extends EJBHome {
    public FruitShop create() throws CreateException, RemoteException;
}
Session Bean
import java.rmi.RemoteException;
import javax.ejb.*;

public class FruitShopBean implements SessionBean {
    public void ejbActivate() throws EJBException, RemoteException {}
    public void ejbPassivate() throws EJBException, RemoteException {}
    public void ejbRemove() throws EJBException, RemoteException {}
    public void setSessionContext(SessionContext context)
        throws EJBException, RemoteException {}
		
    public String getBanana() {
        return "You get a banana.";
    }
}
別忘了在 classpath 上加上 javaEE 的 jar 檔,以免編譯錯誤。如果照上面的步驟安裝的話,在 ${tomcat.home}/webapps/openejb/lib/ 下可以找到 openEJB 提供的 javaee-api-5.0-3.jar

然後建立一個 ejb-jar.xml 如下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise 
JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd">

<ejb-jar>
    <enterprise-beans>
    <session>
		<ejb-name>FruitShop</ejb-name>
		<remote>FruitShop</remote>
        <home>FruitShopHome</home>                
        <ejb-class>FruitShopBean</ejb-class>
        <session-type>Stateless</session-type>
        <transaction-type>Bean</transaction-type>
        <security-identity>
            <description></description>
            <use-caller-identity></use-caller-identity>
        </security-identity>
    </session>
    </enterprise-beans>
</ejb-jar>

部署 EJB
編譯成 jar 檔後,放在 ${tomcat.home}/conf/openejb.xml 中設定的路徑下
<!--
#
#  The <Deployments> element can be used to configure file
#  paths where OpenEJB should look for ejb jars or ear files.
#
#  See http://openejb.apache.org/3.0/deployments.html
#
#  The below entry is simply a default and can be changed or deleted
-->
<!-- 可以用相對路徑的方式, 相對於openejb.base設定的路徑 -->
<Deployments dir="ejb/" /> 
<!-- 或是指定一個目錄, 讓openEJB自動搜尋相關jar檔 -->
<Deployments dir="D:/Tomcat/ejb/" /> 
<!-- 最後還可以直接指定目標jar檔 -->
<Deployments jar="D:/Tomcat/ejb/FruitShop.jar" /> 

測試 client 端呼叫 EJB
根據官方的說明文件,用 Remote Client with HTTP (in tomcat) 的方式來呼叫 EJB
import java.util.Properties;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;

public class EJBTest {
	public static void main(String args[]) throws Exception {
		Properties p = new Properties();
		p.put("java.naming.factory.initial",
		    "org.apache.openejb.client.RemoteInitialContextFactory");
		p.put("java.naming.provider.url",
		    "http://localhost:8080/openejb/ejb");
		InitialContext ctx = new InitialContext(p);
		Object ref = ctx.lookup("FruitShopRemoteHome");
		FruitShopHome home = (FruitShopHome)
		    PortableRemoteObject.narrow(ref, FruitShopHome.class);
		FruitShop remote = home.create();
		System.out.println(remote.getBanana()); //You get a banana.
	}
}

沒意外的話應該可以從 console 看到輸出。
另外... 關於 eclipse helios 怎樣將 EJB project 輸出 client 用的 jar 檔還有待研究。

2011/08/25

javascript中的null與undefined的差異

有些時候會作 object detection 來判斷物件是否存在(像是在 function 內判斷傳進來的參數存在與否),而判斷的標準用 null 或 undefined 其實會有些微的不同。
ECMA-262標準(*註1)中關於這部份的敘述:
  • undefined value

    primitive value used when a variable has not been assigned a value.
  • null value

    primitive value that represents the intentional absence of any object value.
簡單說,null 表示無值,而 undefined 表示沒有定義的變數。
下面就分別對其做了一些測試:

null
雖然 type 是 objet,null 但不算一個真正的物件,不是空字串'',也不是 false 值,但如果直接當成條件式來判斷,會得到 false
alert(null); //null
alert(typeof null); //object
alert(null == ''); //false
alert(null == false); //false
alert(null instanceof Object); //false
alert(null ? true: false); //false
理所當然的也不能對 null 的屬性作操作
var nullObj = null;
nullObj.property = 'value'; //丟出TypeError
alert(nullObj.someProperty) //丟出TypeError

undefined
undefined 的 type 就是 undefined,當成條件式使用一樣會得到 false
alert(typeof undefined); //undefined
alert(undefined == ''); //false
alert(undefined == false); //false
alert(undefined instanceof Object); //false
alert(undefined ? true: false); //false
沒宣告過的變數,雖然 type 是 undefined,但存取它則會丟出 ReferenceError
alert(typeof undefinedObj); //undefined
alert(undefinedObj); //丟出ReferenceError
alert(undefinedObj.property); //丟出ReferenceError
alert(undefinedObj === undefined); //丟出ReferenceError
宣告卻沒給值的變數,預設會給它 undefined 的值
var undefinedObj;
alert(typeof undefinedObj); //undefined
alert(undefinedObj); //undefined
alert(undefinedObj.property); //丟出ReferenceError
alert(undefinedObj === undefined); //true
定義過的物件,屬性未定義(宣告)前都屬於"宣告卻沒給值的變數",也就是帶 undefined 值
var definedObj = new Object();
alert(typeof definedObj.property) //undefined
alert(definedObj.property); //undefined
alert(definedObj.property === undefined); //true
從 function 內接收的參數如果有宣告且呼叫時未傳入值,等同宣告卻沒給值的變數
function methodCall(parameter) {
 alert(typeof parameter); //undefined
 alert(parameter); //undefined
}
methodCall();
微妙的是,用 == 運算子做比較,null 跟 undefined 會得到 true
alert(null == undefined) //true
alert(null === undefined); //false

結論
null 跟 undefined 就像 true/false 一樣表示一個值;而用於 object detection 的時候,遇到一個不知道宣告與否的變數,用 typeof 來判斷比較好。因為除非你自己設 null 值,不然在程式中遇到的機率很低。(目前我只知道 document.getElementById() 如果找不到元素,會回傳 null)
//如果用其他運算子來判斷,遇到未宣告過的變數會直接丟error
if(typeof obj == 'undefined') {
 //do something here...
}

//真的擔心會遇到null值,可以再加上後面的判斷式
if(typeof obj == 'undefined' && obj) {
 //do something here...
}
對於已宣告的物件,則可以直接當成條件式來使用
var definedObj = new Object();
//javascript中&&和||只要運算到可以得出結果就會忽略剩下的運算式
//所以如果otherObj無定義,則直接跳出此if判斷
if(definedObj.otherObj && definedObj.otherObj.someFunction) {
 definedObj.otherObj.someFunction();
}

//函式呼叫
function methodCall(parameter) {
 if(parameter) {
  //do something with parameter...
 }
}

註1:ECMA-262 是 javascript 的標準,由 ECMA 這個組織定的

2011/08/19

CSS測試 - 文章小圖示及段落標題

H1 TEXT

H2 TEXT

H3 TEXT

H4 TEXT

H5 TEXT
H6 TEXT

This is a quote block.

SPAN TEST
The background-image property sets the background image for an element.The background of an element is the total size of the element, including padding and border (but not the margin).By default, a background-image is placed at the top-left corner of an element, and repeated both vertically and horizontally.

DIV TEST
The background-image property sets the background image for an element.The background of an element is the total size of the element, including padding and border (but not the margin).By default, a background-image is placed at the top-left corner of an element, and repeated both vertically and horizontally.

javascript的物件導向程式設計

javascript 是一種 prototype-based 的語言,在 OOP 的寫法上相較於 java 更為麻煩,所以特別研究了一下幾篇文章,順便紀錄一下心得,免得以後忘記。


物件的建立
一種是透過 new 關鍵字
//這個牽涉到建構子,稍後會提到
var banana = new Fruit();
另一種則是使用 {} 宣告一個物件
//隱含的建立一個 javascript 內建的 Object 物件
var banana = {
 //物件屬性
 color: 'yellow',
 size: '15cm',
 //物件方法
 destroy: function() {
  alert('Noooooooooo!');
 }
};


建構子(Constructor)、物件屬性和物件方法的宣告
使用 function 宣告不只是可以建立函式,也可以建立一個建構子。而屬性和方法可以在建構子內宣告,這種方式相當於在每個物件建立時才把屬性和方法附加在物件上;不過這樣做有個缺點,如果父類別採用這種方式宣告,當存取父類別的屬性或方法時,會變成 undefined,因為屬性和方法是附加在每個建立好的物件上,而不是類別本身。
//建構子乍看之下就只是一個函式而已
function Fruit(name, color) {
 //私用屬性(private)
 //其實就是區域變數的概念而已
 var secret = 'You cant see this property.';
 //公用屬性(public)
 this.name = name;
 this.color = color; 
 
 //私用方法(private)
 function doNothing() {};
 //公用方法(public)
 this.changeColor = function(newColor) {
  this.color = newColor;
 }
}
之後便可以透過 new 來產生新的類別實體
//建立類別實體
var fruit1 = new Fruit('banana', 'yellow');
var fruit2 = new Fruit('apple', 'red');

alert(fruit1 instanceof Fruit); //true
alert(fruit1 === fruit2); //false
alert(fruit1.color); //yellow

fruit1.changeColor('green');
alert(fruit1.color); //green

alert(typeof fruit1.secret); //undefined
alert(typeof fruit1.doNothing); //undefined
alert(typeof fruit1.stillDoNothing); //undefined
但是如果直接對變數宣告屬性或方法,只會附加在目標變數指向的物件上,而不是類別本身。
//直接對變數宣告屬性
fruit1.looks = 'Like a banana.';
var fruit3 = new Fruit('grape', 'purple');

alert(fruit1.looks); //Like a banana.
alert(typeof fruit3.looks); //undefiend


javascript 的 prototype 與繼承(Inheritance)
在 javascript 中物件的繼承跟 prototype 這個屬性有很大的關係。

在取出物件的屬性時,會先去檢查該物件是否有直接定義目標屬性;若是沒有,則檢查該物件的 prototype 物件有沒有定義,如果還是沒有,會往 prototype chain (透過繼承將 prototype 鏈結起來)的上一層找,直到根部的 prototype 為止。特別要注意的是所有的物件都會隱含的參照到一個 prototype,但是只有 Function 物件(建構子)可以直接存取 prototype 屬性。

而物件的屬性和方法比較正規的宣告方式也是使用 prototype 來宣告,這樣做有一個好處,就是可以設定屬性初始值。
//改用繼承的方式來寫
//水果建構子
function Fruit(name) {
 this.name = name ? name : this.name;
}

//使用 prototype 作宣告並初始化
Fruit.prototype.name = 'Fruit';
Fruit.prototype.inFruit = 'inFruit';
Fruit.prototype.show = function() {
 alert(this.name);
};

//香蕉建構子,香蕉是一種水果我跟你說
function Banana() {
 //呼叫父類別的建構子
 this.super.call(this, 'Banana');
}

//繼承自 Fruit
//也是把 Banana 接上 Fruit 的 prototype chain
Banana.prototype = new Fruit();
//修正 Banana 的建構子指標,根據網路上的說法他會指向 Fruit 的建構子
//但測試後,有沒有這行其實沒差,因為上面已經宣告過 Banana 的建構子了
Banana.prototype.constructor = Banana;

//使用 prototype 作宣告並初始化
Banana.prototype.super = Fruit; //父類別參照(super)得自己宣告,
                                //因為 javascript 不提供直接參
                                //照到父類別的變數
Banana.prototype.color = 'yellow';
Banana.prototype.changeColor = function(newColor) {
 this.color = newColor;
};
//覆寫父類別的function
Banana.prototype.show = function() {
 alert(this.name + '\'s color is ' + this.color + '.');
};

var banana = new Banana();
alert(banana instanceof Banana); //true
alert(banana instanceof Fruit); //true
alert(banana.inFruit); //inFruit
banana.show(); //Banana's color is yellow.

banana.changeColor('green');
banana.show(); //Banana's color is green.
若要存取父類別的屬性或方法,還是要透過 prototype 來取得。
alert('Parent\'s name is ' + banana.super.prototype.name + '.');
                                       //Parent's name is Fruit.
banana.super.prototype.show(); //Fruit

2011/08/18

javascript的Function.apply()與Function.call()

有時候寫網頁時,DOM 元素的事件處理函式,裡面都是用 this 來存取觸發事件的元素;若想直接呼叫這些處理函式,則 this 會變成指向 window 物件。在沒有辦法指定 this 的情況下要如何直接呼叫這些函式呢?老師有沒有蕉你要多翻API...還愣著做什麼!

原生的 Function 物件提供了兩個函式 apply 與 call,可以在呼叫函式時改變函式內 this 指向的物件
Function.apply(obj, [argument1, argument2, ..., argumentN]);
Function.call(obj, argument1, argument2, ..., argumentN);

而這兩個函式的差異只在參數的傳遞方式而已,第一個參數皆為 this 的指標,而呼叫函式時的參數,apply 用陣列把參數包起來,call 則是依序列出參數
function alertColor(name) {
 //若直接呼叫此函式,this 會指向底層的 window 物件
 alert(name + '\'s color is ' + this.color + '!');
} 

function Fruit(color) {
 this.color = color; 
}

var banana = new Fruit('yellow');
var apple = new Fruit('red');
alertColor.apply(banana, ['Banana']);
alertColor.call(apple, 'Apple');

據說這在實作物件階層時特別有用(呼叫父類別的函式?),有機會研究在補上。

jQuery與其他library的相容問題

以前傻傻的沒碰過,到了真的遇到其他 javascript library 也用 $ 當變數的時候,要改起來的時候還真有些麻煩咧...好在官方也不是傻子,他們提供了一個方便又好用的函式 jQuery.noConflict,可以將 $ 的控制權釋放出去,於是就有了下列幾種解法:


jQuery 程式碼包成函式
jQuery.noConflict();
(function($) {
   //Write you jQuery code here, use $ instead of jQuery...
   $('div').html('testing');
})(jQuery);
將程式碼包在一個函式裡面,將 jQuery 作為一個參數傳遞進去,在接收時就可以任意命名了 (這裡是使用預設的 $ ),缺點是只有在這函式內可以使用 $,有 scope 上的限制


用其他別名取代 $
var j = jQuery.noConflict();
//Do something with jQuery...
j('input').hide();
//Do something with another library's $
$.something();
宣告一個變數接受 jQuery.noConflict() 回傳的值,就可以直接當成別名使用了


直接使用原來的變數名稱
jQuery.noConflict();
//Do something with jQuery...
jQuery('input').hide();
//Do something with another library's $
$.something();
跟上面那個沒什麼不同,變數名稱的差異而已


自己寫的網頁應該是不容易發生這種問題,倒是如果要改別人寫的東西,要加東加西時最好先看一下用了哪些 library,避免浪費無謂的時間。