作者: 肖菁 (dev2dev ID: powerise)
[文章摘要] Workshop8.1中對EJB的支持非常不錯,各種可視化的環境讓EJB的開發和配置變得簡單起來,美中不足的是,沒有提供對BMP的嚮導支持。作者通過實踐提供了一種讓Workshop支持BMP開發和配置的方法,希望能夠幫助更多的人享受Workshop帶來的方便。
關鍵詞: Workshop BMP 可視化
Wokrshop8.1是Weblogic Platform 8.1中BEA提供的開發工具,裏面提供的可視化EJB開發、配置環境讓開發者通過簡單的操作就可以完成複雜的EJB的開發和配置工作,和Weblogic的無縫集成(直接編譯、自動部署功能)讓他迅速成爲我開發基於Weblogic平臺企業應用的好幫手。然而,不久就發現有點美中不足,Workshop中只提供了對CMP、Session Bean、Message-driven Bean的嚮導支持。雖然CMP/CMR越來越受歡迎,而且作用範圍越來越大,然而很多時候BMP仍然是一種選擇,因爲BMP提供的靈活性讓人無法拒絕。優秀的工具和優秀的原理無法結合給人的感覺是痛苦的,怎麼辦?這個問題困擾了我很久,以直到有一天,我無意中看到了Workshop中的這個選項: ,我決定試一試,看看這個功能是否可以幫助我實現在Workshop中編譯和部署BMP。很幸運,我成功了,而且發現原來BMP也可以在Workshop中得到更完美的支持,包括可視化的增加、修改其它配置內容,當然還有些其他的高級支持內容。下面是我的操作全過程,希望幫助大家盡情享受Workshop帶給我們的方便。
1演示環境
系 統: windows 2000
Weblogic platform: 8.1 sp2 英文版
數 據 庫: MySQL 4.0.18
2準備數據庫
我的數據庫名爲CRM,在裏面建立了一個簡單的表,建表語句如下:
create table company (
id int(5) primary key,
name char(250) not null,
address char(250)
);
注:爲了讓示例簡單,我這裏沒有定義ID字段爲自增長類型。
3簡單的代碼準備工作
1. 編寫簡單的源代碼
現在我們需要準備一些簡單的代碼,以便完成接下來的工作,作者的源代碼根目錄是C:/bmp(後面將引用爲%BMP_HOME%),這些代碼分爲三類,
- 繼承自EJBHome的CompanyHome
代碼如下:
package org.vivianj.wls.examples.bmp;
import javax.ejb.EJBHome;
public interface CompanyHome
extends EJBHome{}
- 繼承自EJBObject的Company
代碼如下:
package org.vivianj.wls.examples.bmp;
import javax.ejb.EJBObject;
public interface Company
extends EJBObject{}
- 繼承自EntityBean的CompanyBean
代碼如下:
package org.vivianj.wls.examples.bmp;
import java.rmi.RemoteException;
import javax.ejb.EJBException;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
import javax.ejb.RemoveException;
public class CompanyBean implements EntityBean {
private EntityContext ctx;
public void setEntityContext(EntityContext arg0)
throws EJBException, RemoteException {
ctx = arg0;
}
public void unsetEntityContext() throws EJBException, RemoteException {
ctx = null;
}
public void ejbRemove() throws RemoveException, EJBException, RemoteException {}
public void ejbActivate() throws EJBException, RemoteException {}
public void ejbPassivate() throws EJBException, RemoteException {}
public void ejbLoad() throws EJBException, RemoteException {
}
public void ejbStore() throws EJBException, RemoteException {}
}
2. 編譯源代碼
設置好環境變量後,將前面編寫的內容編譯一下
3. 將代碼打包成.jar文件
現在請用winzip或者其他工具將%BMP_HOME%下的所有文件(其實只需要其中的所有.class文件就可以了)打包成.jar文件(作者生成了org.jar,放在%BMP_HOME%下)
注:可以包含或者不包含裏面的.java源代碼文件
4生成EJB描述文件
代碼準備好了,現在需要生成必須的EJB描述文件,這個工作當然要藉助於Welbogic Builder了。
打開Weblogic Builder,打開剛生成的org.jar,Weblogic Builder提示
表示這個.jar文件中沒有配置描述符文件,是否需要Weblogic Builder幫你生成一個,單擊“是(Y)”按鈕,Weblogic Builder就幫你生成了需要的配置描述符。單擊Weblogic Builder界面上工具欄中的 圖標,將修改信息保存到.jar文件中。
5導入Workshop
前面的步驟進展順利的話,現在就可以將BMP導入到Workshop中了:
1. 打開Workshop,建立一個Application,然後建立一個EJB project
2. 在生成的EJB Project上單擊右鍵,在彈出的上下文菜單中選擇 彈出的界面如下
界面中需要指定兩個內容,第一個是ejb-jar文件,我們這裏就是被weblogic builder修改過的文件,也就是%BMP_HOME%/org.jar,下面的那個選擇EJB中類的原文件所在的目錄,這裏就是%BMP_HOME%,選擇完了請單擊”next”按鈕
3. 接下來的界面中直接單擊”Finish”按鈕結束
現在你可以看到Workshop中多了一個EJB,但是它默認打開了源代碼編輯視圖,而且沒有design視圖
6激動人心的時刻
不要緊,雖然是源代碼視圖,接下來發生的事情仍然讓人激動不已:
1. 請將鼠標放到public class CompanyBean implements EntityBean 這行代碼上,你看到了什麼?右邊的Property Editor視圖中出現了和這個EJB配置相關的內容:
現在,你如果需要修改這個EJB的配置內容,可以直接在這個Property Editor中修改了(可愛的可視化操作回來了)。
2. 在源代碼視圖中單擊右鍵,你看到什麼?這個界面也許更有震撼力:
不但可以修改EJB相關的配置內容,你還可以通過這個功能可增加資源引用、訪問角色定義、環境變量等相關內容。
3. 修改CompanyBean的代碼,增加如下內容:
/*
* Created on 2004-3-23
*
* To change the template for this generated file go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
package org.vivianj.wls.examples.bmp;
import java.rmi.RemoteException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import javax.ejb.EJBException;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
import javax.ejb.ObjectNotFoundException;
import javax.ejb.RemoveException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
/**
* @author Administrator
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
* @ejbgen:jndi-name remote="CompanyBean"
* @ejbgen:file-generation local-class="false" remote-home-name="CompanyHome" remote-class-name="Company" remote-home="true" local-home="false" remote-class="true"
* @ejbgen:entity default-transaction="Required" prim-key-class="java.lang.Integer" read-timeout-seconds="600" table-name="companys" delay-database-insert-until="ejbPostCreate" concurrency-strategy="Database" reentrant="False" persistence-type="bmp" ejb-name="CompanyBean" trans-timeout-seconds="0" data-source-name="MyDataSource" max-beans-in-cache="1000"
* @ejbgen:resource-ref jndi-name="datasource/mysql" sharing-scope="Shareable" auth="Container" type="javax.sql.DataSource" name="jdbc/mysqlds"
* @ejbgen:env-entry value="mysqlds" type="java.lang.String" name="poolName"
* @ejbgen:env-entry value="company" type="java.lang.String" name="tablename"
*/
public class CompanyBean implements EntityBean {
private EntityContext ctx;
private DataSource dataSource;
private String tableName;
private Integer id;
private String name;
private String address;
/* (non-Javadoc)
* @see javax.ejb.EntityBean#setEntityContext(javax.ejb.EntityContext)
*/
public void setEntityContext(EntityContext arg0)
throws EJBException, RemoteException {
ctx = arg0;
try{
Context envCtx = (Context) new InitialContext().lookup("java:/comp/env");
tableName = (String)envCtx.lookup("tablename");
String poolName = (String)envCtx.lookup("poolName");
dataSource = (DataSource) envCtx.lookup("/jdbc/"+poolName);
}
catch(NamingException ne){
ne.printStackTrace();
}
}
/* (non-Javadoc)
* @see javax.ejb.EntityBean#unsetEntityContext()
*/
public void unsetEntityContext() throws EJBException, RemoteException {
ctx = null;
}
/* (non-Javadoc)
* @see javax.ejb.EntityBean#ejbRemove()
*/
public void ejbRemove()
throws RemoveException, EJBException, RemoteException {
Connection conn = null;
Statement stmt = null;
try{
conn = dataSource.getConnection();
stmt = conn.createStatement();
stmt.executeUpdate("delete from "+ this.tableName +" where id=" + this.id);
}
catch(Exception e){
e.printStackTrace();
}
finally{
try{
if (stmt != null ) stmt.close();
if (conn != null ) conn.close();
}
catch(Exception e){
e.printStackTrace();
}
}
}
/* (non-Javadoc)
* @see javax.ejb.EntityBean#ejbActivate()
*/
public void ejbActivate() throws EJBException, RemoteException {}
/* (non-Javadoc)
* @see javax.ejb.EntityBean#ejbPassivate()
*/
public void ejbPassivate() throws EJBException, RemoteException {}
/* (non-Javadoc)
* @see javax.ejb.EntityBean#ejbLoad()
*/
public void ejbLoad() throws EJBException, RemoteException {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
this.id = (Integer) ctx.getPrimaryKey();
conn = dataSource.getConnection();
stmt = conn.createStatement();
rs = stmt.executeQuery("select * from "+this.tableName+" where id="+this.id);
while (rs.next()){
this.name = rs.getString("name");
this.address = rs.getString("address");
}
}
catch(Exception e){
e.printStackTrace();
}
finally{
try{
if (rs != null ) rs.close();
if (stmt != null ) stmt.close();
if (conn != null ) conn.close();
}
catch(Exception e){
e.printStackTrace();
}
}
}
/* (non-Javadoc)
* @see javax.ejb.EntityBean#ejbStore()
*/
public void ejbStore() throws EJBException, RemoteException {
Connection conn = null;
Statement stmt = null;
try{
conn = dataSource.getConnection();
stmt = conn.createStatement();
stmt.executeUpdate("update "+ this.tableName +
" set name="+this.name+",address="+this.name+" where id=" + this.id);
}
catch(Exception e){
e.printStackTrace();
}
finally{
try{
if (stmt != null ) stmt.close();
if (conn != null ) conn.close();
}
catch(Exception e){
e.printStackTrace();
}
}
}
public Integer ejbCreate(int p_id,String p_name,String p_address){
this.id = new Integer(p_id);
this.name = p_name;
this.address = p_address;
Connection conn = null;
Statement stmt = null;
try{
conn = dataSource.getConnection();
stmt = conn.createStatement();
stmt.executeUpdate("insert into "+this.tableName+" values("
+ p_id + "," + p_name + "," + p_address + ")");
}
catch(Exception e){
e.printStackTrace();
}
finally{
try{
if (stmt != null ) stmt.close();
if (conn != null ) conn.close();
}
catch(Exception e){
e.printStackTrace();
}
}
return this.id;
}
public void ejbPostCreate(int p_id,String p_name,String p_address){}
public Integer ejbFindByPrimaryKey(Integer p_id){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
int i = 0;
try{
conn = dataSource.getConnection();
stmt = conn.createStatement();
rs = stmt.executeQuery("select * from "+this.tableName+" where id="+p_id);
while (rs.next()){
i = Integer.parseInt(rs.getString("id"));
this.name = rs.getString("name");
this.address = rs.getString("address");
}
if (i==0) throw new ObjectNotFoundException("沒有編號爲"+i+"的公司紀錄");
}
catch(Exception e){
e.printStackTrace();
}
finally{
try{
if (rs != null ) rs.close();
if (stmt != null ) stmt.close();
if (conn != null ) conn.close();
}
catch(Exception e){
e.printStackTrace();
}
}
return new Integer(i);
}
/**
* @ejbgen:remote-home-method
*/
public Collection ejbFindCompanysByName(String p_name){
Collection c = new ArrayList();
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
conn = dataSource.getConnection();
stmt = conn.createStatement();
rs = stmt.executeQuery("select * from "+this.tableName+" where name like %" + p_name + "%");
CompanyBean company = null;
while (rs.next()){
company = new CompanyBean();
company.setId(Integer.parseInt(rs.getString("id")));
company.setName(rs.getString("name"));
company.setAddress(rs.getString("address"));
c.add(company);
}
}
catch(Exception e){
e.printStackTrace();
}
finally{
try{
if (rs != null ) rs.close();
if (stmt != null ) stmt.close();
if (conn != null ) conn.close();
}
catch(Exception e){
e.printStackTrace();
}
}
return c;
}
/**
* @ejbgen:remote-method
*/
public void setId(int p_id){
this.id = new Integer(p_id);
}
/**
* @ejbgen:remote-method
*/
public void setName(String p_name) {
this.name = p_name;
}
/**
* @ejbgen:remote-method
*/
public void setAddress(String p_address) {
this.address = p_address;
}
/**
* @ejbgen:remote-method
*/
public int getId() {
return this.id.intValue();
}
/**
* @ejbgen:remote-method
*/
public String getName() {
return this.name;
}
/**
* @ejbgen:remote-method
*/
public String getAddress() {
return this.address;
}
}
現在編譯你的EJB工程,然後打開生成xx.jar(你的EJB工程編譯後生成的.jar文件),看看這個.jar文件裏面的描述配置符,是否有以下內容:
<entity>
<ejb-name>CompanyBean</ejb-name>
<home>org.vivianj.wls.examples.bmp.CompanyHome</home>
<remote>org.vivianj.wls.examples.bmp.Company</remote>
<ejb-class>org.vivianj.wls.examples.bmp.CompanyBean</ejb-class>
<persistence-type>Bean</persistence-type>
<prim-key-class>java.lang.Integer</prim-key-class>
<reentrant>False</reentrant>
<env-entry>
<env-entry-name>tablename</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>company</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>poolName</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>mysqlds</env-entry-value>
</env-entry>
<resource-ref>
<res-ref-name>jdbc/mysqlds</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
</entity>
注: 作者的配置符中增加了環境變量和資源引用,如果你沒有,應該看起來像這個樣子:
<entity>
<ejb-name>CompanyBean</ejb-name>
<home>org.vivianj.wls.examples.bmp.CompanyHome</home>
<remote>org.vivianj.wls.examples.bmp.Company</remote>
<ejb-class>org.vivianj.wls.examples.bmp.CompanyBean</ejb-class>
<persistence-type>Bean</persistence-type>
<prim-key-class>java.lang.Integer</prim-key-class>
<reentrant>False</reentrant>
</entity>
4. 現在啓動你的服務器,你會發覺這個EJB已經和你Workshop中的其他EJB一樣被配置到服務器中了。
7更進一步
好了,現在BMP已經集成到了Workshop中,配置已經和CMP的支持基本一致了,開發怎麼辦?不要急,深入地瞭解一下Workshop的工作原理,一切是可以解決的:
1. 配置工作做了,如何實現遠程方法呢?這是個難題,需要對Workshop有更深入的瞭解,不過做起來當然不困難,如果你用Workshop開發過Sessuin Bean,你應該知道你的本地方法和遠程方法是通過什麼來區別的,對,是標籤:
- 這是表示一個方法是遠程(Remote)訪問方法的標籤
/**
* @ejbgen:remote-method
*/
- 這是表示一個方法是本地(Locale)訪問方法的標籤
/**
* @ejbgen:local-method
*/
這個方法同樣適用於BMP,你可以用這種標籤設定力的業務方法是遠程方法還是本地方法
注:作者個人猜測,Workshop中應該是使用了Xdoclet框架來編譯、生成EJB項目。
2. 我們的所有準備工作和操作都是針對遠程調用的,本地調用呢?Workshop仍然會自動幫你生成本地調用接口,只是需要增加配置內容,請再次將鼠標指向public class CompanyBean implements EntityBean所在的行,右邊的Property Editor會出現這樣的內容:
選中裏面的Locale EJB後面的多選框,輸入需要的JNDI Name/Bean Class Name/Home Class Name屬性,然後編譯,你會發現,這些類(CompanyLocale/CompanyLocalHome)仍然會自動生成。也就意味着,你也可以同時生成需要的遠程和本地調用類。
8總結
Workshop8.1的可視化支持非常棒,以至於開發基於Weblogic平臺應用的時候愛不釋手,然而他不提供BMP的嚮導,使我開發BMP的時候只能藉助記事本和ANT,還得忍受不停的自己重新啓動應用,於是一直想找到一種解決辦法,無意中的靈感解決了這個問題,所以記錄了下來,希望能夠幫助大家更好地享受Workshop,也希望更多的人發覺Workshop中的更好的功能,讓Workshop發揮它的潛力。
作者信息:
姓名: 肖菁 (dev2dev ID: powerise)
簡介: 作者目前是湖南省長沙鐵道學院科創計算機系統集成有限公司軟件中心軟件工程師,IBM developerworks/BEA dev2dev撰稿人,主要研究J2EE編程技術、Web Service技術以及他們在websphere、weblogic、apache平臺上的實現,擁有IBM 的 Developing With Websphere Studio證書。歡迎大家訪問作者的個人網站: guilaida.go.nease.net