Learn EJB (1)

初學EJB Session Bean理解的一些概念和一些低級的問題。IDE是RAD6。記性不好,記錄下來備忘。

一些概念

覺得下面網址上這段話不錯,先用一下:

http://www-128.ibm.com/developerworks/cn/java/j-namespace/index.html#resources

名稱空間上下文初始上下文子上下文這些術語都是有關位置的 ―― 是從客戶機的角度看時 EJB 組件所在的概念性的位置。將一個名稱空間想像爲一個城鎮,城鎮中的商店由 EJB home接口(我們將在稍後討論它)表示。上下文是城鎮中的一個位置。初始上下文是您開始時所在的位置 ―― 就像它是到城鎮的道路。而子上下文是街道名。
home接口(home interface)和遠程接口(remote interface企業 JavaBean 組件有三個部分。首先是 bean 代碼本身。然後是home接口,它定義了創建您自己的 EJB bean 的方法。home接口是在名稱空間中發佈的。當您有了home接口後,就可以調用 Create() 以從應用服務器獲得遠程接口。獲得了遠程接口後,就可以調用構成實際的 EJB 代碼的方法了。
如何將這些術語應用到您的城鎮模擬中去呢?到達正確的城鎮並找到正確的地址後,您需要走進商店或者按鈴(調用Create())。這個過程對於您要去的所有商店都是一樣的,不過,您所收到的響應取決於是由誰來提供服務 ―― 比如是一位屠夫、一位麪包師還是一位燭臺製作者。這個響應代表了遠程接口。每個人都是不同的並且可以要求他提供不同的東西。您必須知道與您交談的人(即 bean)的職業才能提出正確的問題(即調用正確的方法) ―― 向一位屠夫要一條麪包可不妥當。
CosNamingLDAP JNDI Java 命名和目錄接口(Java Naming and Directory Interface JNDI)提供了一個標準接口,它指明您需要如何與名稱空間交互。我們所提到的LDAPCosNaming就是 JDNI 名稱空間類型。現在擴展我們的比喻:JNDI 是城鎮的模板,而 CosNaming 和 LDAP 是特定的城鎮。它們以相似的方式操作,但是有不同的佈局。

EJB的各種stub/tie java代碼都是在佈署的時候生成的, 所以如果是提示什麼stub/tie找不到或出錯什麼的, 不妨把這些stub文件刪掉, 重新編譯佈署試試. 因爲自動生成的是stub/tie java代碼, 所以用project clean是無法clean的.

新建EJB項目時,有一個選項 Create an EJB Client JAR Project to hold the client interfaces and classes ,如下:

對這個選項的描述如下:

What is a EJB client project? It is a plain Java project that holds the remote/local home and component interfaces, and generated stub classes. The project contains minimal classes and interface for a EJB client application to compile and execute.
How does the EJB project refer to the home and component interfaces in the EJB client project? The EJB client project is setup in the enterprise application project (Test Application) as a utility JAR project. The EJB project - Test EJB - has a Java JAR dependency on the client project. (The concept of a utility JAR project is fairly old and a discussion of this is beyond the scope of this tutorial).
The EJB client layer (i.e. the Web project) should build a dependency on the client JAR file only and never to the actual EJB JAR file. The EJB JAR file is installed only in the application server machine where the EJB module is deployed. The client JAR file can be freely distributed anywhere.

這樣有一個好處, EJB和使用EJB的客戶端都用到的類比如VO, 就可以定義在EJB client project中. 這在小型軟件中是很方便實用的做法。

使用EJB的客戶端想要遠程調用EJB的方法,比如是普通的Java Application,它必須包含EJB Client的jar包(或整個EJB的jar包), j2ee.jar(因爲用到javax.ejb, javax.naming, javax.rmi等包裏的類), namingclient.jar(jndi的一些東西), implfactory.properties和jndi.properties (兩個都放在代碼目錄src下編譯後自動會拷貝到bin下或class路徑下, 只要程序運行時能找到, implfactory.properties位於RAD6/runtimes/base_v51/bin/wsinstance/propdefaults, jndi.properties位於RAD6/rwd/eclipse/plugins/com.ibm.etools.utc_6.0.0/IBMUTC/IBMUTC.ear/UTC.war/WEB-INF/jndi.properties ).

不包含namingclient.jar可能出現如下錯誤:

Exception in thread "main" javax.naming.ConfigurationException: The property com.ibm.ws.naming.wsn.factory.initial is not set. The most likely cause is that the jar which contains the file com/ibm/websphere/naming/jndiprovider.properties cannot be found by the class loader.
      at com.ibm.websphere.naming.WsnInitialContextFactory.init_implClassCtor(WsnInitialContextFactory.java:192)
      at com.ibm.websphere.naming.WsnInitialContextFactory.getInitialContext(WsnInitialContextFactory.java:110)
      at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:675)
      at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:257)
      at javax.naming.InitialContext.init(InitialContext.java:233)
      at javax.naming.InitialContext.<init>(InitialContext.java:209)
      at Main.getContext(Main.java:22)
      at Main.main(Main.java:28)

lookup時只能引用EJB在server中的jndi name(iiop://localhost:2809/ejb/secondEJB/SecondHelloHome)來訪問. java:comp/env前綴是j2ee上下文的標準前綴, 這個客戶端並沒有.

javax.rmi.PortableRemoteObject.narrow方法的作用只是爲了檢測, 只是出於安全上的考慮. 在不能構成所要對象時會拋出ClassCastException異常.

“Checks to ensure that an object of a remote or abstract interface type can be cast to a desired type.”

具體一份客戶端討問EJB的代碼如下:

package tony.firstEJBTest;

import java.rmi.RemoteException;
import java.util.Properties;

import javax.ejb.CreateException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;

import tony.firstEJB.HelloWorld;
import tony.firstEJB.HelloWorldHome;

/**
 * 
@author p465890
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 
*/

public class HelloWorldTest
{
    
// not used here, because the context has been set in jndi.properties
    private static InitialContext getContext() throws NamingException
    
{
        Properties props 
= new Properties();
        props.put(InitialContext.INITIAL_CONTEXT_FACTORY, 
"com.ibm.websphere.naming.WsnInitialContextFactory");
        props.put(InitialContext.PROVIDER_URL, 
"iiop://localhost:2809/");
        
return new InitialContext(props);
    }

    
    
public static void main(String[] args) throws NamingException, RemoteException, CreateException
    
{
        
// InitialContext initial = getContext();
        InitialContext initial = new InitialContext();
        Object objref 
= initial.lookup("iiop://localhost:2809/ejb/tony/firstEJB/HelloWorldHome");
        HelloWorldHome home 
= (HelloWorldHome)PortableRemoteObject.narrow(objref, HelloWorldHome.class);
        
//HelloWorldHome home = (HelloWorldHome)objref;
        if (objref == (HelloWorldHome) PortableRemoteObject.narrow(objref, HelloWorldHome.class))
        
{
            System.out.println(
"yes");
        }

        
else
        
{
            System.out.println(
"no");
        }

        HelloWorld hello 
= home.create();
        System.out.println(hello.getSomething());
    }

}

若客戶端是J2EE Client Application或Web程序或什麼垃圾容器中建的工程, 一般都包含了J2EE Context, 所以只要寫客戶端連接代碼就可以了, 什麼包啊, .properties配置文件都自動包含進來了.

EJB2.1的項目,除了重建,不知有沒更好地辦法改用EJB2.0。

在客戶端要使用服務端EJB時,創建初始下下文時,如下語句:

InitialContext initial = new InitialContext();

錯誤提示:

javax.naming.ConfigurationException: Name space accessor for the java: name space has not been set. Possible cause is that the user is specifying a java: URL name in a JNDI Context method call but is not running in a J2EE client or server environment.
      at com.ibm.ws.naming.java.javaURLContextFactory.isNameSpaceAccessable(javaURLContextFactory.java:98)
      at com.ibm.ws.naming.urlbase.UrlContextFactory.getObjectInstance(UrlContextFactory.java:73)
      at javax.naming.spi.NamingManager.getURLObject(NamingManager.java:592)
      at javax.naming.spi.NamingManager.getURLContext(NamingManager.java:541)
      at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:289)
      at javax.naming.InitialContext.lookup(InitialContext.java:361)
      at helloWorldfEJB.HelloWorldClient.main(HelloWorldClient.java:57)

在默認情況下,InitialContext工廠是在jndi.properties中定義的,這個工廠類有默認的服務器 URL 和端口號默認值。這個文件在類路徑中(這一般意味着在本地目錄)或者在您的類路徑中的任何 JAR 中。不同的應用服務器可能在不同的 JAR 文件中提供它們的默認值,WebSphere Application Server 在namingclient.jar中儲存一個默認副本。

所以這個錯誤實際上是沒有指定InitialContext工廠引起的,這可以在程序中指定,也可以通過其他方式,比如jndi.properties中指定, 具體看開頭提到的那個網頁:

http://www-128.ibm.com/developerworks/cn/java/j-namespace/index.html#resources

於是可以用下面的方法創建InitialContext實例:

Properties props = new Properties();
props.put(InitialContext.INITIAL_CONTEXT_FACTORY,  
"com.ibm.websphere.naming.WsnInitialContextFactory");
props.put(InitialContext.PROVIDER_URL, 
"iiop://localhost:2809/");
InitialContext initialContext 
= new InitialContext(props);

上面設的值com.ibm.websphere.naming.WsnInitialContextFactory和iiop://localhost:2809/在jndi.properties文件可以找到,也可以在Client Test(RAD有提供,直接佈署EJB就會跳出)中JNDI Properties中查看,如下圖:

在Test Client下還可以測試佈署好的EJB,具體便不說了,圖如下:

另外值得提下的是用開頭引用頁面上講的方法,通過命令行配置InitialContext工廠,即運行tnameserv.exe方法,會出現錯誤提示大概如下:

Exception in thread "main" org.omg.CORBA.DATA_CONVERSION:   minor code: 5 4F4D0001  completed: No

網上查了下只知道跟字符的編碼有關,沒有細究。

發佈了35 篇原創文章 · 獲贊 0 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章