隨筆-109  評論-187  文章-25  trackbacks-0
           

          ClassLoader in Tomcat (http://rosonsandy.blogdriver.com/rosonsandy/871539.html


          1 - Tomcat 的類載入器的結構

          Tomcat Server 在啟動的時候將構造一個ClassLoader樹,以保證模塊的類庫是私有的
          Tomcat Server
          ClassLoader結構如下:

          ??????? +-----------------------------+?

          ??????? |???????? Bootstrap?????????? |?

          ??????? |???????????? |???? ??????????|?

          ??????? |????????? System???????????? |?

          ??????? |???????????? |?????????????? |?

          ??????? |????????? Common???????????? |?

          ??????? |???????? /????? \??????????? |?

          ??????? |???? Catalina? Shared??????? |?

          ??????? |?????????????? /??? \??????? |?

          ???? ????|????????? WebApp1? WebApp2?? |?

          ??????? +-----------------------------+

          其中:
          - Bootstrap -
          載入JVM自帶的類和$JAVA_HOME/jre/lib/ext/*.jar
          - System -
          載入$CLASSPATH/*.class
          - Common -
          載入$CATALINA_HOME/common/...,它們對TOMCAT和所有的WEB APP都可見
          - Catalina -
          載入$CATALINA_HOME/server/...,它們僅對TOMCAT可見,對所有的WEB APP都不可見
          - Shared -
          載入$CATALINA_HOME/shared/...,它們僅對所有WEB APP可見,對TOMCAT不可見(也不必見)
          - WebApp -
          載入ContextBase?/WEB-INF/...,它們僅對該WEB APP可見

          2 - ClassLoader 的工作原理

          每個運行中的線程都有一個成員contextClassLoader,用來在運行時動態地載入其它類
          系統默認的contextClassLoadersystemClassLoader,所以一般而言java程序在執行時可以使用JVM自帶的類、$JAVA_HOME/jre/lib/ext/中的類和$CLASSPATH/中的類
          可以使用
          Thread.currentThread().setContextClassLoader(...); 更改當前線程的contextClassLoader,來改變其載入類的行為

          ClassLoader 被組織成樹形,一般的工作原理是:
          1)
          線程需要用到某個類,于是contextClassLoader被請求來載入該類
          2) contextClassLoader
          請求它的父ClassLoader來完成該載入請求
          3)
          如果父ClassLoader無法載入類,則contextClassLoader試圖自己來載入

          注意 WebApp?ClassLoader的工作原理和上述有少許不同:
          它先試圖自己載入類(在ContextBase?/WEB-INF/...中載入類),如果無法載入,再請求父ClassLoader完成

          由此可得:
          -
          對于WEB APP線程,它的contextClassLoaderWebApp?ClassLoader
          -
          對于Tomcat Server線程,它的contextClassLoaderCatalinaClassLoader

          3 類的查找

          ClassLoader類中loadClass方法為缺省實現,用下面的順序查找類:

          1、調用findLoadedClass方法來檢查是否已經被加載。如果沒有則繼續下面的步驟。

          2、如果當前類裝載器有一個指定的委托父裝載器,則用委托父裝載器的loadClass方法加載類,也就是委托給父裝載器加載相應的類。

          3、如果這個類裝載器的委托層級體系沒有一個類裝載器加載該類,則使用類裝載器定位類的特定實現機制,調用findClass方法來查找類。

          4?- 部分原代碼分析

          4.1 - org/apache/catalina/startup/Bootstrap.java

          Bootstrap中定義了三個classloader:commonLoader,catalinaLoader,sharedLoader.三者關系如下:

          // 注意三個自己定置的ClassLoader的層次關系:?

          ??????????? // systemClassLoader (root)?

          ?? ?????????? //?? +--- commonLoader?

          ??????????? //????????? +--- catalinaLoader?

          ??????????? //????????? +--- sharedLoader

          Tomcat Server 線程的起點
          構造ClassLoader樹,通過Thread.currentThread().setContextClassLoader(catalinaLoader)設置當前的classloader為catalinaLoader。
          載入若干類,然后轉入org.apache.catalina.startup.Catalina類中

          4.2 org.apache.catalina.loader.StandardClassLoader.java

          通過看loadClass這個方法來看tomcat是如何加載類的,順序如下:

          (0) Check our previously loaded class cache查找已經裝載的class
          ??????? clazz = findLoadedClass(name);

          (1)?If a system class, use system class loader通過系統classloader來裝載class

          ??????? ClassLoader loader = system;
          ??????????? clazz = loader.loadClass(name);

          (2) Delegate to our parent if requested如果有代理則使用父類classloader

          ??????????? ClassLoader loader = parent;
          ??????????? if (loader == null)
          ??????????????? loader = system;
          ????????????clazz = loader.loadClass(name);

          (3) Search local repositories 查找本地類池,比如$CATALINA_HOME/server

          ?????????? clazz = findClass(name);

          (4) Delegate to parent unconditionally 默認使用代理裝載器

          [ 查看代碼]

          4.3 - org/apache/catalina/startup/ClassLoaderFactory.java

          根據設置創建并返回StandardClassLoader的實例

          [ 查看代碼]

          4.4 - org/apache/catalina/loader/StandardClassLoader.java

          類載入器

          4.5 - org/apache/catalina/startup/SecurityClassLoad.java

          該類僅包含一個靜態方法,用來為catalinaLoader載入一些類

          [ 查看代碼]

          Appendix - 參考

          [1] http://jakarta.apache.org/tomcat/ 中的Tomcat 4.1.x文檔Class Loader HOW-TO

          在一個 JVM 中可能存在多個 ClassLoader ,每個 ClassLoader 擁有自己的 NameSpace 。一個 ClassLoader 只能擁有一個 class 對象類型的實例,但是不同的 ClassLoader 可能擁有相同的 class 對象實例,這時可能產生致命的問題。如 ClassLoaderA ,裝載了類 A 的類型實例 A1 ,而 ClassLoaderB ,也裝載了類 A 的對象實例 A2 。邏輯上講 A1=A2 ,但是由于 A1 A2 來自于不同的 ClassLoader ,它們實際上是完全不同的,如果 A 中定義了一個靜態變量 c ,則 c 在不同的 ClassLoader 中的值是不同的。

          posted @ 2006-04-18 08:48 小小程序程序員混口飯吃 閱讀(451) | 評論 (0)編輯 收藏

          轉自:http://m47121.blogchina.com/2555077.html

          ??????????????????????????????????????

          編程

            我們將首先編寫遠程對象,并將代碼保存為名字為AddServer.Java的文件:

          import Java.rmi.*;

          public interface AddServer extends Remote {

          public int AddNumbers(int firstnumber,int secondnumber) throws RemoteException;

          }

            我們來看看上面的代碼。首先,為了使用其內容,我們導入rmi包。然后,我們創建一個擴展了Java.rmi中遠程接口的接口。所有的遠程對象必須擴展該遠程接口,我們將該遠程接口稱為AddServer。在該遠程對象中,有一個名字為AddNumbers的方法,客戶端可以調用這一方法。我們必須記住的是,所有的遠程方法都需要啟動RemoteException方法,有錯誤發生時就會調用該方法。

            下面我們開始編寫遠程對象的實現。這是一個實現遠程對象并包含有所有方法代碼的類,將下面的代碼保存為名字為AddServerImpl.Java的文件:

          import Java.rmi.*;

          public class AddServerImpl extends UnicastRemoteObject implements AddServer {
          public AddServerImpl() {
          super();
          }
          public int AddNumbers(int firstnumber,int secondnumber) throws RemoteException {
          return firstnumber + secondnumber;
          }
          }

            首先,我們導入rmi包,然后創建一個擴展UnicastRemoteObject和實現創建的遠程對象的類;其次,我們可以為類創建一個缺省的構建器。我們還了解了AddNumbers方法的代碼,它啟動RemoteException。這樣我們就覆蓋了創建的遠程對象中的方法。AddNumbers方法的代碼非常好理解,它接受2個整型參數,然后相加并返回它們的和。

            至此,我們已經有了二個Java文件:遠程對象和遠程對象的實現。下面我們將使用Javac命令編譯這二個文件:

            編譯遠程對象:

          C:\jdk\bin\Javac workingdir\AddServer.Java

            編譯遠程對象實現:

          C:\jdk\bin\Javac workingdir\AddServerImpl.Java

            這樣,就會達到二個Java文件和二個類文件,下面我們將創建stub和skeleton。為了創建stub和skeleton文件,我們必須使用rmic編譯器編譯遠程對象實現文件。

            用Rmic編譯遠程對象實現文件:

          C:\jdk\bin\rmic workingdir\AddServerImpl.Java

            然后,我們就會發現多了2個新建的類文件,它們分別是AddServerImpl_Stub.class 和AddServerImpl_Skel.class 。

            The Coding (Contd.)

            我們已經編譯了所有的源代碼,下面我們來創建客戶端和服務器端,將下面的代碼保存為名字為RmiServer.Java的文件:

          import Java.rmi.*;
          import Java.net.*;

          public class RmiServer {
          public static void main (String args[]) throws RemoteException, MalformedURLException {
          AddServerImpl add = new AddServerImpl();
          Naming.rebind("addnumbers",add);
          }
          }

            首先,我們導入Java.rmi包和Java.net包。另外,我們還使用throws從句捕獲任何異常。我們從對象中得出遠程對象實現,使用rebind方法將字符串addnumbers與該對象綁定。下面的例子顯示了綁定的含義:
          從現在開始,無論何時客戶端要調用遠程對象,使用字符串addnumbers就可以實現。rebind方法有二個參數:第一個參數是字符串變量,第二個參數是遠程對象實現類的對象。

            下面我們來創建客戶端,將下面的代碼保存為名字為RmiClient.Java的文件:

          import Java.rmi.*;
          import Java.net.*;

          public class RmiClient {
          public static void main(String args[]) throws RemoteException, MalformedURLException {
          String url="rmi://127.0.0.1/addnumbers";
          AddServer add;
          add = (AddServer)Naming.lookup(url);
          int result = add.AddNumbers(10,5);
          System.out.println(result);
          }
          }

            首先,我們導入Java.rmi包和Java.net包,并使用throws從句捕獲所有必要的異常。然后通過利用Naming類中的靜態lookup方法從遠程對象中得到一個對象。(這也是我們無需從Naming類中得到一個對象并調用它。而只使用類名字的原因。)

            lookup方法接受遠程對象的完整的URL名字,該URL由完整的機器IP地址以及與對象綁定的字符串(也誻對象的綁定名)組成。在調用遠程對象時,我們使用了RMI協議。lookup方法向我們返回一個對象,在能夠使用它前,我們必須將它的數據類型轉換為與遠程對象的數據類型一致。

          Since we have both our server and client source ready, let's compile them both:

            至此,我們已經有了服務器端和客戶端的源代碼,下面我們來編譯這二個源文件:

            編譯遠程服務器:

          C:\jdk\bin\Javac workingdir\RmiServer.Java

            編譯遠程客戶端:

          C:\jdk\bin\Javac workingdir\RmiClient.Java

            在對我們的代碼進行測試前,還必須首先啟動RMI Registry。RMI Registry存儲有所有綁定的數據,沒有它,RMI就不能正常地運行!

            啟動Rmi Registry服務器:

          C:\jdk\bin\start rmiregistry

            我們會注意到,這時會出現一個空白的DOS提示符窗口,這表明Rmi Registry服務器在運行,注意不要關閉該窗口。然后,我們首先在一個DOS提示符窗口中運行Rmi服務器,然后在另一個DOS提示符窗口中運行Rmi客戶端。

            啟動RMI服務器:

          C:\jdk\bin\Java workingdir\RmiServer

            啟動RMI客戶端:

          C:\jdk\bin\Java workingdir\RmiClient

            如果一切正常,我們應該能夠得到15這個輸出。我們向AddNumbers方法輸入10和5二個數字,該方法將這二者加起來,并將其和15返回給我們。如果得到了15這個輸出,說明我們已經成功地執行了一個遠程方法。當然,在這里,我們并沒有執行真正意義上的遠程方法,因為我們的計算機既是服務器,又是客戶機。如果有計算機網絡,我們就可以方便地進行執行遠程方法的試驗了。

          posted @ 2006-04-14 09:17 小小程序程序員混口飯吃 閱讀(1814) | 評論 (0)編輯 收藏
          ?
          Spring中事務的定義:
          一、Propagation :

          對于特定的方法或方法命名模式,代理的具體事務行為由事務屬性驅動,如下面的例子所示:
          <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
          <prop key="store*">PROPAGATION_REQUIRED</prop>

            key屬性確定代理應該給哪個方法增加事務行為。這樣的屬性最重要的部份是傳播行為。有以下選項可供使用:

          • PROPAGATION_REQUIRED--支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。
          • PROPAGATION_SUPPORTS--支持當前事務,如果當前沒有事務,就以非事務方式執行。
          • PROPAGATION_MANDATORY--支持當前事務,如果當前沒有事務,就拋出異常。
          • PROPAGATION_REQUIRES_NEW--新建事務,如果當前存在事務,把當前事務掛起。
          • PROPAGATION_NOT_SUPPORTED--以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
          • PROPAGATION_NEVER--以非事務方式執行,如果當前存在事務,則拋出異常。

          ??????前六個策略類似于EJB CMT:常量名相同,因此,對EJB開發人員來說,應該立刻就感到熟悉。第七個(PROPAGATION_NESTED)是Spring所提供的一個特殊變量。它要求事務管理器或者使用JDBC 3.0 Savepoint API提供嵌套事務行為(如Spring的DataSourceTransactionManager),或者通過JTA支持嵌套事務。

              

          二、Isolation Level(事務隔離等級):
          1、Serializable:最嚴格的級別,事務串行執行,資源消耗最大;
          2、REPEATABLE READ:保證了一個事務不會修改已經由另一個事務讀取但未提交(回滾)的數據。避免了“臟讀取”和“不可重復讀取”的情況,但是帶來了更多的性能損失。
          3、READ COMMITTED:大多數主流數據庫的默認事務等級,保證了一個事務不會讀到另一個并行事務已修改但未提交的數據,避免了“臟讀取”。該級別適用于大多數系統。
          4、Read Uncommitted:保證了讀取過程中不會讀取到非法數據。

          spring中的Isolation屬性:
          1、ISOLATION_DEFAULT :使用當前數據源的默認級別
          2、ISOLATION_READ_UNCOMMITTED :Dirty reads, non-repeatable reads, and phantom reads can occur.
          3、ISOLATION_READ_COMMITTED :Dirty reads are prevented; non-repeatable reads and phantom reads can occur.
          4、ISOLATION_REPEATABLE_READ:Dirty reads and non-repeatable reads are prevented; phantom reads can occur.
          5、ISOLATION_SERIALIZABLE:Dirty reads, non-repeatable reads, and phantom reads are prevented.

          三、readOnly
          事務屬性中的readOnly標志表示對應的事務應該被最優化為只讀事務。這是一個最優化提示。在一些情況下,一些事務策略能夠起到顯著的最優化效果,例如在使用Object/Relational映射工具(如:Hibernate或TopLink)時避免dirty checking(試圖“刷新”)。

          四、Timeout

          ??????在事務屬性中還有定義“timeout”值的選項,指定事務超時為幾秒。在JTA中,這將被簡單地傳遞到J2EE服務器的事務協調程序,并據此得到相應的解釋。

          事務劃分策略

          1、推薦在業務層使用事務,這樣可以允許業務層捕獲導致rollback的異常,并拋出恰當的業務層異常;不在dao層使用事務是因為這會限制了dao重用其他事務需求,并且dao層沒有實現業務邏輯,并且原子性也是業務層的概念。

          spring聲明性事務的劃分:
          1、有四個地方需要配置:The four participants are transaction manager, proxy factory, transaction interceptor, and a set of transaction attributes.




          2、使用ProxyFactoryBean/Transaction Interceptor(transactionInterceptor)配置spring事務

          以下為配置實例:

          <!-- The DBCP DataSource -->
            <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"  
                  destroy-method="close">
              <property name="driverClassName">
                <value>${jdbc.driverClassName}</value>
              </property>
              <property name="url"><value>${jdbc.url}</value></property>
              <property name="username"><value>${jdbc.username}</value></property>
              <property name="password"><value>${jdbc.password}</value></property>
            </bean>
             
            <!-- The DAO class -->
            <bean id="dao" 
          class="org.springframework.prospring.ticket.dao.jdbc.JdbcBoxOfficeDao">
              <property name="dataSource">
                <ref local="dataSource"/>
              </property> 
            </bean>
             
            <!-- The transactionmanager to use for regular non JTA datasource -->
          ??<bean id="transactionManager"????class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
              <property name="dataSource">
                <ref local="dataSource"/>
              </property> 
            </bean>
             
            <!-- TransactionInterceptor -->
          ??<bean id="transactionInterceptor" ????class="org.springframework.transaction.interceptor.TransactionInterceptor">????<property name="transactionManager">??????<ref bean="transactionManager"/>????</property>????<property name="transactionAttributeSource">??????<value>org.springframework.prospring.ticket.service.BoxOffice.get*=PROPAGATION_SUPPORTS,readOnlyorg.springframework.prospring.ticket.service.BoxOffice.allocate*=PROPAGATION_REQUIRED
                </value>
              </property>
            </bean>  
             
            <!-- Transactional proxy for the primary business object -->
          ??<bean id="boxOffice" ????????class="org.springframework.aop.framework.ProxyFactoryBean">????<property name="target">??????<ref local="boxOfficeTarget"/>????</property>????<property name="proxyInterfaces">??????<value>org.springframework.prospring.ticket.service.BoxOffice</value>????</property>????<property name="interceptorNames">??????<value>transactionInterceptor</value>????</property>
            </bean>  
             
            <!-- Business Object -->
            <bean id="boxOfficeTarget" 
              class="org.springframework.prospring.ticket.service.BoxOfficeImpl">
              <property name="boxOfficeDao">
                <ref local="dao"/>
              </property> 
            </bean>

          3、使用TransactionProxyFactoryBean配置spring事務
          以下為配置實例:

            <!-- The DBCP DataSource -->
            <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 
                  destroy-method="close">
              <property name="driverClassName">
                <value>${jdbc.driverClassName}</value>
              </property>
              <property name="url"><value>${jdbc.url}</value></property>
              <property name="username"><value>${jdbc.username}</value></property>
              <property name="password"><value>${jdbc.password}</value></property>
            </bean>
             
            <!-- The DAO class -->
            <bean id="dao"
          class="org.springframework.prospring.ticket.dao.jdbc.JdbcBoxOfficeDao">
              <property name="dataSource">
                <ref local="dataSource"/>
              </property> 
            </bean>
             
            <!-- The transactionmanager to use for regular non JTA datasource -->
          ??<bean id="transactionManager"????class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
              <property name="dataSource">
                <ref local="dataSource"/>
              </property> 
            </bean>
             
            <!-- Transactional proxy and the primary business object -->
          ??<bean id="boxOffice" ?? class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">????<property name="transactionManager"><ref bean="transactionManager"/></property>????<property name="target">??????<bean class="org.springframework.prospring.ticket.service.BoxOfficeImpl">
                  <property name="boxOfficeDao">
                    <ref local="dao"/>
                  </property> 
                </bean>
              </property>
          ????<property name="transactionAttributes">??????<props>????????<prop key="get*">PROPAGATION_SUPPORTS,readOnly</prop>????????<prop key="allocate*">PROPAGATION_REQUIRED</prop>??????</props>????</property>
            </bean>  

          4、使用BeanNameAutoProxyCreator配置spring事務
          如果有大量的bean需要使用事物,那么只要在配置文件中提供bean name給BeanNameAutoProxyCreator,spring就會個給該bean提供事務代理,配置實例如下:

            <!-- The DBCP DataSource -->
            <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 
                destroy-method="close">
              <property name="driverClassName">
                <value>${jdbc.driverClassName}</value>
              </property>
              <property name="url"><value>${jdbc.url}</value></property>
              <property name="username"><value>${jdbc.username}</value></property>
              <property name="password"><value>${jdbc.password}</value></property>
            </bean>
             
            <!-- The DAO class -->
            <bean id="dao"
          class="org.springframework.prospring.ticket.dao.jdbc.JdbcBoxOfficeDao">
              <property name="dataSource">
                <ref local="dataSource"/>
              </property> 
            </bean>
             
            <!-- The transactionmanager to use for regular non JTA datasource -->
          ??<bean id="transactionManager"????class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
              <property name="dataSource">
                <ref local="dataSource"/>
              </property> 
            </bean> 
             
            <!-- TransactionInterceptor -->
          ??<bean id="transactionInterceptor" ????????class="org.springframework.transaction.interceptor.TransactionInterceptor">????<property name="transactionManager">??????<ref bean="transactionManager"/>????</property>????<property name="transactionAttributeSource">??????<value>org.springframework.prospring.ticket.service.BoxOffice.get*=PROPAGATION_SUPPORTS,readOnlyorg.springframework.prospring.ticket.service.BoxOffice.allocate*=PROPAGATION_REQUIRED??????</value>????</property>
            </bean>  
             
            <!-- BeanNameAutoProxyCreator -->
          <bean id="autoProxyCreator" ???class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">??<property name="interceptorNames">????<value>transactionInterceptor</value>??</property>??<property name="beanNames">????<list>??????<idref local="boxOffice"/>????</list>??</property>
          </bean>  
             
          <!-- Business Object -->
          <bean id="boxOffice"
             class="org.springframework.prospring.ticket.service.BoxOfficeImpl">
            <property name="boxOfficeDao">
              <ref local="dao"/>
            </property> 
          </bean>
          posted on 2006-04-04 09:06 loocky 閱讀(0)
          posted @ 2006-04-13 09:04 小小程序程序員混口飯吃 閱讀(441) | 評論 (0)編輯 收藏
          ?
          Kodo是BEA公司收購SolarMetric公司后獲得的持久層框架項目,以前只是支持JDO標準,2006年2月13日,BEA公司宣布發布Kodo項目的最新版本——Kodo 4.0.0 Early Access 4,Kodo 4.0.0 EA4支持EJB3和JDO2兩個標準,本文中,我們將首先學習和了解KODO EJB,了解如何使用Kodo EJB完成開發工作。

          ??? 我們將按照兩種不同的情況講述如何使用Kodo EJB進行開發,一種是通過命令行工具,另外一種是在Eclipse中使用Ant任務。

          ??? 關于EJB3和JDO2的更多內容請大家查看文章最后參考資源中的相關內容。

          ??? 為什么使用Kodo EJB

            
          在Kodo EJB框架中,對象和關系數據庫之間的映射(對象-表,對象屬性-字段等)都是使用JDK5.0中的最新特性—注釋(Annotation)來提供,不再需要提供額外的配置文件。

          ??? 根據EJB3規范的要求,Kodo EJB除了支持在EJB容器中使用滿足重量級企業應用的需求之外,也支持在普通Java應用中采用,提供輕量級的持久層框架。只不過當我們在EJB容器中使用Kodo EJB時我們需要更多的工作以便滿足EJB容器的要求。

          ??? 下載、安裝Kodo

            準備工作

            
          由于Kodo是基于注釋機制的框架,我們必須使用JDK5.0完成開發工作。所以下載、安裝Kodo之前,請確保您已經下載和安裝了JDK5.0.

          ??? 為了演示的需要,我們選擇MySQL數據庫作為持久化的目標數據庫,請大家自己到www.mysql.com下載最新的MySQL數據庫后安裝。

          ??? 下載、安裝Kodo

            Kodo的最新版本是Kodo 4.0.0 Early Access 4,目前大家可以到http://www.solarmetric.com/去下載試用版本,下載時需要注冊,你會得到30天的License.

          ??? 將下載的壓縮文件解壓到c:/kodo4目錄下(后面將使用%KODO_HOME%來引用這個目錄),打開%KODO_HOME%/bin/kodocmd.cmd文件,將其中的KODODIR設置為您的Kodo的安裝目錄,將JDKHOME設置為Java的安裝目錄。

          ??? Kodo EJB實例

            
          上面的工作完成以后,我們就可以開發Kodo EJB應用了,下面的例子將主要講述如何開發一個輕量級的Kodo EJB例子,支持在Java應用中的調用,而不依賴于EJB容器,關于如何在EJB容器中配置使用Kodo EJB是一個大的課題,作者將另外撰文詳細論述。

          ??? 下面我們將用一個簡單的例子來說明創建Kodo EJB應用的典型步驟,實例中我們將創建名為Book的持久化對象,該對象將被持久化到本地的MySQL數據庫中。

          ??? 請大家注意的是,下面的說明都基于Windows 2000操作系統,如果您使用其它操作系統,可能需要做出相應的變更。

          ??? 建立工程目錄在C:盤根目下創建名為KodoExamples的目錄,我們所有的類文件和配置都放在這個目錄下。

          ??? 創建持久化類新創建持久化類Book,為了說明的簡單,該類只有兩個屬性:id和name,其中id是書的編號(編號由MySQL數據庫自動生成),name屬性表示書的名稱。持久化類的全部代碼、注釋和說明如下: ackage org.vivianj.kodo.examples.beans;import javax.persistence.Basic;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;import javax.persistence.Inheritance;import javax.persistence.InheritanceType;import javax.persistence.Table;

          ??? /** * Book 用于表征系統中的書籍對象,他有兩個屬性id - 書籍編號,書籍編號將由MySQL數據庫自動生成name - 書名*/ /* Entity注釋表示該類是持久化類,的name屬性是該實體在查詢中對應的唯一名稱,默認是類名 */(name = "Book")

          ??? /* Table注釋的name屬性指定該持久化類對應的數據表的名稱,默認數據表名和類名保持一致,為了增強代碼的可移植性,建議大家在name屬性中使用大寫英文字母 */(name = "BOOKS")

          ??? /* Inheritance注釋的strategy確定了持久化對象和數據表之間的關系,可選擇項包括SINGLE_TABLE、JOINED和TABLE_PER_CLASS,我們這里采用TABLE_PER_CLASS */(strategy = InheritanceType.TABLE_PER_CLASS)

          ??? public class Book { /* Id注釋表示該字段是標識字段 */

          ??? /* GeneratedValue注釋定義了該標識字段的產生方式,我們的演示系統中id由MySQL數據庫字段自動生成,因此選擇GenerationType.IDENTITY */(strategy = GenerationType.IDENTITY)

          ??? /* Column注釋的name屬性定義了該類屬性對應的數據字段的名稱,為了最大限度保持系統和數據庫之前的獨立性,建議使用大寫字符 */(name = "ID")

          ??? public int id;

          ??? /* Basic注釋表示該屬性是基本屬性 */

          ??? /* Column注釋的name屬性定義了該類屬性對應的數據字段的名稱,為了最大限度保持系統和數據庫之前的獨立性,建議使用大寫字符 */(name = "NAME")

          ??? public String name = null;準備數據庫在MySQL數據庫中新建名為Kodo的數據庫。

          ??? 準備配置文件在C:KodoExamples新建META-INF目錄,隨后在該目錄下新建kodo.xml和persistence.xml文件。

          ??? a)

          ??? Kodo.xml文件中提供訪問數據庫所需要的細節信息、使用Kodo所需要的授權(License)內容、Kodo運行時的日志管理等。

          ??? <?xml version="1.0"?> <persistence> <persistence-unit name=""> <properties> <!—— Kodo的序列號,請輸入您下載或者購買Kodo時得到的License ——> <property name="kodo.LicenseKey" value="093D-BF3F-C10E-0F8F-0F00" />

          ??? <!—— 以下是訪問數據庫時需要提供的信息 ——> <property name="kodo.ConnectionURL" value="jdbc:mysql://localhost/kodo" /> <property name="kodo.ConnectionDriverName" value="org.gjt.mm.mysql.Driver" /> <property name="kodo.ConnectionUserName" value="root" /> <property name="kodo.ConnectionPassword" value="root" />

          ??? <!—— 設置Kodo運行過程中的日志級別 ——> <property name="kodo.Log" value="DefaultLevel=WARN, Runtime=INFO, Tool=DEBUG" /> </properties> </persistence-unit> </persistence>

          ??? b)

          ??? persistence.xml提供EJB實體管理所需要的信息,比如確定使用哪種持久化管理器和需要被管理的持久化類。

          ??? <?xml version="1.0"?> <persistence> <persistence-unit name=""> <provider>kodo.persistence.PersistenceProviderImpl</provider> <!—— 需要被Kodo EJB管理的持久化類 ——> <class>org.vivianj.kodo.examples.beans.Book</class> </persistence-unit> </persistence>編譯持久化類打開一個命令行窗口,進入%KODO_HOME%/bin目錄下,執行kodocmd.cmd命令,然后將MySQL的驅動文件使用set classpath=%classpath%;c:/mysql-connector-java-3.1.8-bin.jar這樣的方式加入到CLASSPATH中。

          ??? 執行javac C:KodoExamplesorgvivianjkodoxampleseans*.java編譯持久化類。

          ??? 加強(Enhancer)持久化類KODO中使用加強工具(Enhancer)對持久化類進行調整,支持性能優化、懶惰式裝載等特性。

          ??? 我們可以使用kodoc C:KodoExamplesorgvivianjkodoxampleseans*.java這樣的命令來完成持久化類的加強工作。

          ??? 生成數據庫表KODO中提供了專門的MappingTool工具,能夠自動的根據持久化類及他們之間的關系為我們生成創建數據庫所需要的SQL語句或者直接創建數據表。

          ??? 演示實例中,我們使用kodoc C:KodoExamplesorgvivianjkodoxampleseans*.java完成數據表的創建,命令執行完成后,我們訪問MySQL中的kodo數據庫,可以看到里面已經創建了名為books的數據表。

          ??? 測試一下現在,所有的工作都已經完成,我們可以編寫一段代碼測試一下上面的工作是否有效,在C:kodoExamples目錄下新建Test.java,然后輸入如下內容:

          ??? import javax.persistence.EntityManager;import javax.persistence.EntityManagerFactory;import javax.persistence.Persistence;import javax.persistence.PersistenceContextType;

          ??? import org.vivianj.kodo.examples.beans.Book;

          ??? public class Test {

          ??? public static void main(String[] args) { /* 獲得EJB的實體管理器 */ EntityManagerFactory emf = Persistence.createEntityManagerFactory(null);EntityManager em = emf。createEntityManager(PersistenceContextType.EXTENDED);/* 開始事務 */ em.getTransaction()。begin();

          ??? /* 創建新的持久化對象 */ Book book = new Book();/* 設置Book對象的name屬性 */ book.name = "Kodo入門";/* 持久化對象 */ em.persist(book);

          ??? /* 結束事務 */ em.getTransaction()。commit();em.close();emf.close();}

          ??? }執行Test類,執行完后會發現books表中已經增加了一條新的記錄。

          ??? 在Eclipse中開發Kodo EJB上面的步驟都是基于命令行的,操作起來不是很方便,因此我們需要考慮將Kodo的開發過程和Eclipse開發工具進行集成。

          ??? 分析上面開發Kodo EJB的整個過程,只有步驟 5)加強(Enhancer)持久化類和 6)生成數據表和字段無法在Eclipse中直接實現,查看Kodo的幫助文檔,發現Kodo的發布包中已經提供了對應命令的Ant任務腳本,因此我們可以使用Ant來完成Eclipse和Kodo的集成。

          ??? 我們仍然使用上面的例子來演示如何在Eclipse中開發Kodo EJB,我們打開Eclipse,新建立KodoExamples工程,將%KODO_HOME%ib下面所有jar文件加入到該工程的引用中,將您所使用的數據庫的JDBC驅動jar文件也加入到該工程的引用中。然后請大家參考上面的說明完成前5步工作,下面我們主要描述如何完成Ant配置文件。

          ??? 基本配置內容在工程的當前目錄下,新建build.xml文件,輸入如下內容:

          ??? <project name="kodo enhance" basedir="." default="enhance"> <!— 請將value的值設置為您安裝kodo時選擇的目錄 ——> <property name="KODO_HOME" value="C:kodo4" /> <property name="src" value="." /> <property name="classes" value="." />

          ??? <!——? 準備公用的CLASSPATH路徑 ?

          ??? <path id="build_classpath"> <pathelement location="" /> <!—此處請輸入數據庫驅動文件的路徑,作者使用的MySQL數據庫驅動 ?

          ??? <pathelement location="C:/ mysql-connector-java-3.1.8-bin.jar" />

          ??? <fileset dir="/lib"> <include name="**/*.jar" /> </fileset> </path> </project>編寫完成步驟 6)加強(Enhance)持久化類所需要的任務和這個步驟對應的任務是kodo.ant.PCEnhancerTask類,在%KODO_HOME%srckodont下面我們可以看到PCEnhancerTask類的源代碼。首先使用taskdef加入新的任務類型kodoc,然后調用該任務完成工作。

          ??? 在build.xml文件中增加如下內容:

          ??? <target name="enhance"> <!——? 引入新的Ant任務 ?

          ??? <taskdef name="kodoc" classname="kodo.ant.PCEnhancerTask"> <!——? 引用上面步驟中定義的build_classpath作為CLASSPATH ?

          ??? <classpath refid="build_classpath" /> </taskdef>

          ??? <kodoc jdoEnhance="true"> <fileset dir="."> <!——? 指定需要被加強的持久化類,可以使用通配符* ?

          ??? <include name="**/Book.java" /> </fileset> <!——? 引用上面步驟中定義的build_classpath作為CLASSPATH ?

          ??? <classpath refid="build_classpath" /> </kodoc> </target>編寫完成步驟 7)生成數據庫表所需要的任務Kodo包中為這個任務提供的任務類kodo.jdbc.ant.MappingToolTask在Eclipse中執行時有點問題,我將它修改了一下,主要是修改執行該任務時使用的ClassLoader,現在可以滿足要求了。

          ??? 在當前工程中新建kodo.jdbc.ant.MappingToolTask類(目錄中的類比lib文件中的類有更高的執行級別),類的代碼可以在%KODO_HOME%/src/kodo/jdbc/ant目錄下找到。在類源代碼中找到這兩行。

          ??? if (!MappingTool.run (conf, files, flags, loader))

          ??? throw new BuildException (_loc.get ("bad-conf", "MappingToolTask"));將它修改為:if (!MappingTool.run (conf, files, flags, MappingTool.class.getClassLoader ()))

          ??? throw new BuildException (_loc.get ("bad-conf", "MappingToolTask"));

          ??? 現在,我們在build.xml文件中增加如下內容就可以完成項目中生成持久化類對應的數據庫表了:<target name="create-schema"> <!——? 引入新的Ant任務 ?

          ??? <taskdef name="mappingtool" classname="kodo.jdbc.ant.MappingToolTask"> <!——? 引用上面步驟中定義的build_classpath作為CLASSPATH ?

          ??? <classpath refid="build_classpath" /> </taskdef>

          ??? <mappingtool action="refresh"> <!——? 引用上面步驟中定義的build_classpath作為CLASSPATH ?

          ??? <classpath refid="build_classpath" /> <fileset dir=""> <!——? 指定需要生成數據表的持久化類,可以使用通配符* ?

          ??? <include name="**/Book.java" /> </fileset> </mappingtool> </target>現在你可以將打開Eclipse中的Ant視圖,然后再其中執行Enhance和create-schema任務,完成加強持久化類和創建數據庫表的工作,最后,你可以使用同樣的測試代碼完成代碼的測試工作。

          ??? 總結

            Kodo是BEA最新發布的持久層框架,屏蔽了開發者訪問數據庫的眾多細節,為開發者提供了更加簡單的編程模式,大大較少了開發量,目前Kodo支持EJB3和JDO2標準。BEA承諾在不久的將來將Kodo的核心代碼開源。

          ??? 本文中,作者通過一個簡單的例子,詳細地講解了如何完成Kodo EJB的開發工作,同時講解了如何在Eclipse中集成Kodo完成開發的步驟,并且解決了集成過程中還存在的一個小問題,希望能夠幫助大家更好的學習和使用Kodo.

          ??? 參考資源:

            
          EJB3規范:http://jcp.org/aboutJava/communityprocess/pfd/jsr220/index.html
            JDO2規范:http://jcp.org/aboutJava/communityprocess/pfd/jsr243/index.html
            Kodo在線文檔:http://solarmetric.com/kodo/Documentation/4.0.0EA4/docs/full/html/index.html

          ??? 作者簡介

            
          唯J族(www.vivianj.org)創始人,BEA 杭州User Group負責人,自由撰稿人,開源項目BuildFileDesigner(buildfiledesign.sourceforge.net)和V-Security(v-security.sourceforge.net)創始人。

          posted @ 2006-04-13 09:03 小小程序程序員混口飯吃 閱讀(253) | 評論 (0)編輯 收藏
          ?
          牛牛的站
          JDON
          posted @ 2006-04-13 09:02 小小程序程序員混口飯吃 閱讀(188) | 評論 (0)編輯 收藏
          ?

          JTA(Java Transaction API)是一種高層的,與實現無關的,與協議無關的API,應用程序和應用服務器可以使用JTA來訪問事務。


          JTA是Java Transaction API,是java為處理事務而引入的API,這個事務,包括分布式事務。Java內部的JDBC也提供了事務支持,但那只面向單數據源的本地事務管理。若想做分布式事務管理,必須求助于JTA。



          ???? ?? Transaction不管在J2EE還是.NET領域中都是相當重要的一個組成部分。盡管很多與Transaction相關的概念在兩個不同的平臺中都是相通的,但是它們在Transaction的實現方面卻有著很多的不同。想對.NET下的Transaction有更深入了解的朋友,可以參考 idior 兄寫 Transaction in ADO.net 2.0 。在以下的篇幅里面,我就J2EE中與Transaction相關的幾個概念做些講述。
          ??????? 1.什么是Transaction?所謂Transaction是指一系列不可分割的改動數據庫的操作。在這個解釋中,有三個關鍵詞:一系列不可分割以及改動。僅僅是一個改動數據庫的操作是沒有Transaction可言,只有“一系列”操作(一組SQL語句)才可能組成Transaction;“不可分割”就意味著一致性和完整性,要么這一系列操作全部commit,要么就全部rollback;如果一系列的操作只包含enquiry操作,那么這些操作也不是Transaction。?
          ????????2.在J2EE中,Transaction主要有幾大類,具體有幾種?在J2EE中,Transaction主要有Bean-Managed Transaction和Container-Managed Transaction兩大類。其中在Bean-Managed Transaction中還會分為JDBC Transaction和JTA Transaction兩種。
          ????????3.什么是JDBC Transaction?它有怎樣的特點?JDBC Transaction是指由Database本身去管理的事務。其最大的特點就是通過顯示調用Connection接口的commit和rollback方法來完成事務的提交和回滾。事務結束的邊界是commit或者rollback方法的調用,而開始的邊界則不是那么明顯了,它會開始于組成當前事務的所有statement中的第一個被執行的時候。具體代碼如下:

          class ?CreditDAoImpl?implements?CreditDAO? {
          ????Connection?conn?
          =
          ?getConnection();
          ????
          public ? void ?transfer(Currency?amount,?Account?fromAccount,?Account?toAccount)?throws?CreditException?
          {
          ????????
          try ?
          {
          ?????????????conn.setAutoCommit(
          false
          );
          ?????????????depositToAccount(conn,?toAccount,?amount);
          ?????????????withdrawFromAccount(conn,?fromAccount,?amount);?????
          ?????????????conn.commit();
          ????????}
          ? catch ?(Exception?e)? {
          ????????????
          try ?
          {
          ?????????????????conn.rollback();
          ?????????????????
          throw ? new
          ?CreditException(e.getMessage());
          ????????????}
          ? catch ?(SQLException?e1)? {
          ?????????????????
          throw ? new
          ?CreditException(e.getMessage());
          ????????????}
          ???????????????????????
          ????????}
          ?
          ????}

          }

          ??????? 4.什么是JTA Transaction?它有怎樣的特點呢?JTA Transaction是指由J2EE Transaction manager去管理的事務。其最大的特點是調用UserTransaction接口的begin,commit和rollback方法來完成事務范圍的界定,事務的提交和回滾。JTA Transaction可以實現同一事務對應不同的數據庫,但是它仍然無法實現事務的嵌套。具體的代碼如下[1]

          public ? void ?withdrawCash( double ?amount)? {
          ???UserTransaction?ut?
          =
          ?context.getUserTransaction();
          ???
          try ?
          {
          ??????ut.begin();
          ??????updateChecking(amount);
          ??????machineBalance?
          -=
          ?amount;
          ??????insertMachine(machineBalance);
          ??????ut.commit();
          ???}
          ? catch ?(Exception?ex)? {
          ???????
          try ?
          {
          ??????????ut.rollback();
          ???????}
          ? catch ?(SystemException?syex)?
          {
          ???????????
          throw ? new
          ?EJBException
          ??????????????(
          " Rollback?failed:? " ? +
          ?syex.getMessage());
          ???????}

          ???????
          throw ? new ?EJBException?
          ??????????(
          " Transaction?failed:? " ? +
          ?ex.getMessage());
          ????}

          }

          ??????? 5.什么是Container-Managed Transaction?它又有怎樣的特點呢?Container-Managed Transaction,顧名思義,就是由Container負責管理的Transaction,當然這樣Transaction是出現在EJB的范疇中。Container-Managed Transaction最大的特點是不需要顯式界定事務的邊界,也不需要顯式的提交或者回滾事務,這一切都由Container來替我們完成。我們需要做的就是設定在一個Bean中,哪些方法是跟事務相關的,同時設定它們的Transaction Attribute既可。
          ??????? Transaction的Scope是相當重要的,特別是在一個Bean的方法中調用另外一個Bean的方法。為了便于說明問題,我們把這兩個方法分別稱為methodA和methodB。當methodA調用methodB的時候,methodB在事務的層面上對調用者methodA有怎樣的限制(methodB中是否存在事務)以及methodA如何在事務的層面上實現對methodB的調用(是否需要重新創建一個新的事務來處理methodB的調用)都需要通過Transaction Attribute來設定的。具體的Transaction Attribute有以下六種:Required,RequiresNew,Mandatory,NotSupported,Supports和Never。有關Transaction Attribute的介紹,可以參閱J2EE Tutorial中關于Container-Managed Transaction的介紹

          posted @ 2006-04-13 09:02 小小程序程序員混口飯吃 閱讀(509) | 評論 (0)編輯 收藏
          ?
          讀了幾遍struts的源代碼,感覺struts還是比較容易擴充的,比如對taglib,對action servlet的擴充,對struts-config.xml的擴充都是比較容易的,但是也存在一些問題
          1:struts的taglib感覺是struts里面最不靈活的(用法不靈活)
          2:exception handler是個不錯的設計
          3:對plugin,filter的支持只是對servlet的一個擴充支持而已
          4:? 多種action:baseaction ,dispacth ,mappingdispach,lookup,等等提供多種支持還是不錯的
          5:對于doubleclick和reload的token支持感覺效果一般,并不是很靈活
          6:form的使用還是有些綁定比較死
          7:validator的設計也不是很靈活,感覺在action沒有valitor,很不靈活,設計在Formbean中難道是為了結構?


          有時間讀一下webwork的源代碼,看一下webwork的優缺點
          posted @ 2006-04-13 09:02 小小程序程序員混口飯吃 閱讀(190) | 評論 (0)編輯 收藏
          ?
          ??? 在開發ejb之前,我們先得配置好服務器,我使用的是Weblogic9.0中文版,關于Weblogic9.0配置請看我的另一片文章。配置Weblogic9.0
          ???
          ??? 首先需要配置好eclipse。我這里主要說明weblogic的配置。
          ????ejb19.jpg

          ??? 注意JDK選擇JDK5.0的版本。
          ???
          ??? 順便將weblogic8的配置也貼上來,供大家比較參考
          ??? ejb18.jpg
          ???
          ??? 注意weblogic8的JDK版本必須是JDK1.4。
          ???
          ??? 接下來就開始我們的開發了。

          ejb1.jpg

          ejb2.jpg

          ejb3.jpg

          ejb4.jpg

          ejb5.jpg

          下面就是SessionBean的代碼
          30

          其實就是修改了其中的一個方法:
          ?1?/**
          ?2??????*?An?example?business?method
          ?3??????*
          ?4??????*?@ejb.interface-method?view-type?=?"remote"
          ?5??????*?
          ?6??????*?@throws?EJBException?Thrown?if?method?fails?due?to?system-level?error.
          ?7??????*/
          ?8?????public?String?hello()?throws?EJBException?{
          ?9?????????//?rename?and?start?putting?your?business?logic?here
          10?????????return?new?String("HelloEJBWorld!");
          11?????}
          注意:代碼中的解釋文字不要刪除,因為XDoclet需要。

          配置屬性
          ejb6.jpg

          添加weblogic.jar。我的路徑是:bea\weblogic90\server\lib\weblogic.jar
          ejb16.jpg

          ejb7.jpg

          ejb8.jpg

          ejb9.jpg

          ejb10.jpg

          ejb11.jpg

          ejb12.jpg

          ejb13.jpg

          ejb14.jpg

          ejb15.jpg

          就下來寫EJBTest類:
          ?1?package?com;
          ?2?
          ?3?import?java.rmi.RemoteException;
          ?4?import?java.util.Properties;
          ?5?
          ?6?import?javax.ejb.CreateException;
          ?7?import?javax.naming.Context;
          ?8?import?javax.naming.InitialContext;
          ?9?import?javax.naming.NamingException;
          10?
          11?import?com.interfaces.HelloWorld;
          12?import?com.interfaces.HelloWorldHome;
          13?
          14?public?class?EJBTest?{
          15?
          16?????/**
          17??????*?@param?args
          18??????*/
          19?????public?static?void?main(String[]?args)?{
          20?????????//?TODO?自動生成方法存根
          21?????????Properties?properties=new?Properties();
          22?????????properties.setProperty(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
          23?????????properties.setProperty(Context.PROVIDER_URL,"t3://localhost:7001");
          24?????????
          25?????????Context?context;
          26?????????try?{
          27?????????????context?=?new?InitialContext(properties);
          28?????????????HelloWorldHome?hwh=(HelloWorldHome)context.lookup("ejb/HelloWorld");
          29?????????????HelloWorld?hw=hwh.create();
          30?????????????String?s=hw.hello();
          31?????????????System.out.println(s);
          32?????????}?catch?(NamingException?e)?{
          33?????????????//?TODO?自動生成?catch?塊
          34?????????????e.printStackTrace();
          35?????????}?catch?(RemoteException?e)?{
          36?????????????//?TODO?自動生成?catch?塊
          37?????????????e.printStackTrace();
          38?????????}?catch?(CreateException?e)?{
          39?????????????//?TODO?自動生成?catch?塊
          40?????????????e.printStackTrace();
          41?????????}
          42?????????
          43?????}
          44?
          45?
          46?}
          47?


          最后就是看結果了,先啟動weblogic,然后運行EJBTest程序。

          ejb17.jpg




          作者寫了一篇很好的文章:出處(http://www.aygfsteel.com/rickhunter/articles/25484.html)感謝他的辛苦勞動!
          posted @ 2006-04-13 09:01 小小程序程序員混口飯吃 閱讀(400) | 評論 (1)編輯 收藏
          ?
          BEA WebLogic Server8.1 JMS入門

          時間:2003-11-28
          作者:周海根
          瀏覽次數: 2831 2728
          本文關鍵字:JMS,?基礎,?教程,?概念
          文章工具
          推薦給朋友?推薦給朋友
          打印文章?打印文章

          一. JMS簡介
          1. JMS基本概念

          JMS(Java Message Service)是訪問企業消息系統的標準API,它便于消息系
          統中的Java應用程序進行消息交換,并且通過提供標準的產生、發送、接收消息的接口簡化企業應用的開發。

          2. JMS基本功能
          JMS是用于和面向消息的中間件相互通信的應用程序接口。它既支持點對點(point-to-point)的域,又支持發布/訂閱(publish/subscribe)類型的域,并且提供對下列類型的支持:經認可的消息傳遞,事務型消息的傳遞,一致性消息和具有持久性的訂閱者支持。JMS還提供了另一種方式來對您的應用與舊的后臺系統相集成。
          3. WebLogic JMS Server介紹
          WebLogic Server8.1符合JAVA規范,并通過Sun Microsystems J2EE 1.3認
          證.作為WebLogic的一部分,當然WebLogic JMS Server也完全遵從JMS規范,還支持集群,并可以應用于實際企業系統.下圖是WebLogic JMS Server體系結構.圖中可以看到WebLogic JMS Server主要組件有: WebLogic JMS servers(用于消息通信),Java客戶端,JNDI(用于域名查找), 后備存儲(用于持久消息存儲,基于文件或者JDBC數據庫).

          二. WebLogic JMS特性
          1. 消息通信模型
          JMS 支持兩種消息通信模型:點到點(point-to-point)(PTP)模型和發布/訂閱(Pub/Sub)模型。除了下列不同之外,這兩種消息通信模型非常地相似:
          PTP 模型規定了一個消息只能有一個接收者;Pub/Sub 模型允許一個消息可以有多個接收者。
          2. 消息組成
          消息傳遞系統的中心就是消息。
          一條 Message 分為三個組成部分:
          · 頭(header)是個標準字段集,客戶機和供應商都用它來標識和路由消息。
          · 屬性(property)支持把可選頭字段添加到消息。如果您的應用程序需要不使用標準頭字段對消息編目和分類,您就可以添加一個屬性到消息以實現這個編目和分類。提供 set<Type>Property(...) 和 get<Type>Property(...) 方法以設置和獲取各種 Java 類型的屬性,包括 Object。JMS 定義了一個供應商選擇提供的標準屬性集。
          · 消息的主體(body)包含要發送給接收應用程序的內容。每個消息接口特定于它所支持的內容類型。
          JMS 為不同類型的內容提供了它們各自的消息類型,但是所有消息都派生自 Message 接口。
          · StreamMessage:包含 Java 基本數值流,用標準流操作來順序的填充和讀取。
          · MapMessage:包含一組名/值對;名稱為 string 類型,而值為 Java 的基本類型。
          · TextMessage:包含一個 String。
          · ObjectMessage:包含一個 Serializable Java 對象;能使用 JDK 的集合類。
          · BytesMessage:包含未解釋字節流: 編碼主體以匹配現存的消息格式。
          · XMLMessage: 包含XML內容。擴展TextMessage,XMLMessage 類型的使用,使得消息過濾非常便利。
          3. 消息確認模式
          非事務性會話中,應用程序創建的會話有5 種確認模式,而在事務性會話中,確認模式被忽略。
          五種確認模式說明:
          · AUTO_ACKNOWLEDGE:自動確認模式。一旦接收方應用程序的方法調用從處理消息處返回,會話對象就會確認消息的接收。
          · CLIENT_ACKNOWLEDGE:客戶端確認模式。會話對象依賴于應用程序對被接收的消息調用一個acknowledge()方法。一旦這個方法被調用,會話會確認最后一次確認之后所有接收到的消息。這種模式允許應用程序以一個調用來接收,處理并確認一批消息。注意:在管理控制臺中,如果連接工廠的Acknowledge Policy(確認方針)屬性被設置為"Previous"(提前),但是你希望為一個給定的會話確認所有接收到的消息,那么就用最后一條消息來調用acknowledge()方法。
          · DUPS_OK_ACKNOWLEDGE:允許副本的確認模式。一旦接收方應用程序的方法調用從處理消息處返回,會話對象就會確認消息的接收;而且允許重復確認。在需要考慮資源使用時,這種模式非常有效。注意:如果你的應用程序無法處理重復的消息的話,你應該避免使用這種模式。如果發送消息的初始化嘗試失敗,那么重復的消息可以被重新發送。
          · NO_ACKNOWLEDGE:不確認模式。不確認收到的消息是需要的。消息發送給一個NO_ACKNOWLEDGE 會話后,它們會被WebLogic 服務器立即刪除。在這種模式下,將無法重新獲得已接收的消息,而且可能導致下面的結果:1. 消息可能丟失;和(或者)另一種情況:2. 如果發送消息的初始化嘗試失敗,會出現重復消息被發送的情況。
          · MULTICAST_NO_ACKNOWLEDGE:IP組播下的不確認模式,同樣無需確認。發送給一個MULTICAST_NO_ACKNOWLEDGE會話的消息, 會共享之前所述的NO_ACKNOWLEDGE 確認模式一樣的特征。這種模式支持希望通過IP 組播方式進行消息通信的應用程序,而且無需依賴會話確認提供的服務質量。注意:如果你的應用程序無法處理消息的丟失或者重復,那么你應該避免使用這種模式。如果發送消息的初始化嘗試失敗的話,重復的消息可能會被再次發送。
          注:在上表的5 種確認模式中,AUTO_ACKNOWLEDGE ,DUPS_OK_ACKNOWLEDGE 和
          CLIENT_ACKNOWLEDGE 是JMS 規范定義的,NO_ACKNOWLEDGE 和MULTICAST_NO_ACKNOWLEDGE是WebLogic JMS 提供的。
          三. 配置JMS
          1. 創建連接工廠
          (1) 啟動WebLogic Server8.1,登錄控制臺,選中JMS Connection Factories節點,點擊右邊的" Configure a new JMS Connection Factory...";

          (2) 填寫連接工廠的名稱SendJMSFactory和JNDI名稱SendJMSFactory,點擊"Create";

          (3) 勾上"myserver",將SendJMSFactory應用到myserver;

          2. 定義后備存儲
          (1) 選中JMS Stores節點,點擊右邊的" Configure a new JMS Connection Factory...";

          (2) 填寫文件后備存儲的名稱SendFileStore和目錄Directionary E:\BEA\user_projects\domains\mydomain\sendfilestore,點擊"Create".

          3. 創建JMS服務器
          (1) 選中JMS Servers節點,點擊右邊的" Configure a new JMSServer...";

          (2) 填寫JMS服務器的名稱SendJMSServer和Paging Store設為" SendFileStore",點擊"Create";

          (3) Target選中"myserver",將SendJMSServer應用到myserver.

          4. 創建消息隊列
          (1) 展開"SendJMSServer"節點,點擊" Configure a new JMS Queue...";

          (2) 填寫消息隊列的名稱SendJMSQueue和JNDI名稱SendJMSQueue,點擊"Create";

          四. JMS應用程序
          一個 JMS 應用程序由下列元素組成:
          · JMS 客戶機。 用 JMS API 發送和接收消息的 Java 程序。
          · 非 JMS(Non-JMS)客戶機。 認識到這一點很重要 - 舊的程序經常成為整個 JMS 應用程序的一部分,而且它們的包含應該在設計時預先考慮。
          · 消息。 在 JMS 和非 JMS 客戶機之間交換的消息的格式和內容是 JMS 應用程序設計所必須考慮的部分。
          · JMS 供應商。供應商必須提供特定于其 MOM 產品的具體的實現。
          · 受管對象。 消息傳遞系統供應商的管理員創建了一個對象,它獨立于供應商專有的技術。包括連接工廠ConnectionFactory和目的Destination。
          一種典型的 JMS 程序需要經過下列步驟才能開始消息產生和使用:
          · 通過 JNDI 查找 ConnectionFactory。
          · 通過 JNDI 查找一個或多個 Destination。
          · 用 ConnectionFactory 創建一個 Connection。
          · 用 Connection 創建一個或多個 Session。
          · 用 Session 和 Destination 創建所需的 MessageProducer 和 MessageConsumer。
          · 啟動 Connection。
          下面利用上面配置的JMS資源演示點對點消息發送和接收的過程。
          五. 設計消息發送端
          1. 使用的JMS資源
          服務器URL: t3://localhost:80
          連接工廠: SendJMSFactory
          隊列: SendJMSQueue
          2. 設計步驟
          · 初始化JNDI Tree
          Hashtable env = new Hashtable();
          env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
          env.put(Context.PROVIDER_URL, PROVIDER_URL);
          return new InitialContext(env);
          · lookup ConnectionFactory
          qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
          · lookup Destination
          queue = (Queue) ctx.lookup(queueName);
          · 用 ConnectionFactory 創建Connection
          qcon = qconFactory.createQueueConnection();
          · 用 Connection 創建一個Session
          qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
          · 用 Session 和 Destination 創建MessageProducer
          qsender = qsession.createSender(queue);
          · 啟動 Connection。
          qcon.start();
          · 發送消息
          msg = qsession.createTextMessage();
          msg.setText(message);
          qsender.send(msg);
          3. 源代碼
          package jmssample;

          import java.util.Hashtable;
          import javax.jms.*;
          import javax.naming.Context;
          import javax.naming.InitialContext;
          import javax.naming.NamingException;
          import java.io.BufferedReader;
          import java.io.IOException;
          import java.io.InputStreamReader;

          /** This example shows how to establish a connection
          * and send messages to the JMS queue. The classes in this
          * package operate on the same JMS queue. Run the classes together to
          * witness messages being sent and received, and to browse the queue
          * for messages. The class is used to send messages to the queue.
          *
          * @author Copyright (c) 1999-2003 by BEA Systems, Inc. All Rights Reserved.
          */
          public class QueueSend
          {
          // Defines the JNDI context factory.
          public final static String JNDI_FACTORY="weblogic.jndi.WLInitialContextFactory";

          // Defines the JNDI provider url.
          public final static String PROVIDER_URL=" t3://localhost:80";

          // Defines the JMS connection factory for the queue.
          public final static String JMS_FACTORY="SendJMSFactory";

          // Defines the queue.
          public final static String QUEUE="SendJMSQueue";


          private QueueConnectionFactory qconFactory;
          private QueueConnection qcon;
          private QueueSession qsession;
          private QueueSender qsender;
          private Queue queue;
          private TextMessage msg;

          /**
          * Creates all the necessary objects for sending
          * messages to a JMS queue.
          *
          * @param ctx JNDI initial context
          * @param queueName name of queue
          * @exception NamingException if operation cannot be performed
          * @exception JMSException if JMS fails to initialize due to internal error
          */
          public void init(Context ctx, String queueName)
          throws NamingException, JMSException
          {
          qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
          qcon = qconFactory.createQueueConnection();
          qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
          queue = (Queue) ctx.lookup(queueName);
          qsender = qsession.createSender(queue);
          msg = qsession.createTextMessage();
          qcon.start();
          }

          /**
          * Sends a message to a JMS queue.
          *
          * @param message message to be sent
          * @exception JMSException if JMS fails to send message due to internal error
          */
          public void send(String message) throws JMSException {
          msg.setText(message);
          qsender.send(msg);
          }

          /**
          * Closes JMS objects.
          * @exception JMSException if JMS fails to close objects due to internal error
          */
          public void close() throws JMSException {
          qsender.close();
          qsession.close();
          qcon.close();
          }
          /** main() method.
          *
          * @param args WebLogic Server URL
          * @exception Exception if operation fails
          */
          public static void main(String[] args) throws Exception {
          InitialContext ic = getInitialContext();
          QueueSend qs = new QueueSend();
          qs.init(ic, QUEUE);
          readAndSend(qs);
          qs.close();
          }

          private static void readAndSend(QueueSend qs)
          throws IOException, JMSException
          {
          BufferedReader msgStream = new BufferedReader(new InputStreamReader(System.in));
          String line=null;
          boolean quitNow = false;
          do {
          System.out.print("Enter message (\"quit\" to quit): ");
          line = msgStream.readLine();
          if (line != null && line.trim().length() != 0) {
          qs.send(line);
          System.out.println("JMS Message Sent: "+line+"\n");
          quitNow = line.equalsIgnoreCase("quit");
          }
          } while (! quitNow);

          }

          private static InitialContext getInitialContext()
          throws NamingException
          {
          Hashtable env = new Hashtable();
          env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
          env.put(Context.PROVIDER_URL, PROVIDER_URL);
          return new InitialContext(env);
          }

          }

          六. 設計消息接收端
          1. 使用的JMS資源
          服務器URL: t3://localhost:80
          連接工廠: SendJMSFactory
          隊列: SendJMSQueue
          2. 設計步驟
          · 初始化JNDI Tree
          Hashtable env = new Hashtable();
          env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
          env.put(Context.PROVIDER_URL, PROVIDER_URL);
          return new InitialContext(env);
          · lookup ConnectionFactory
          qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
          · lookup Destination
          queue = (Queue) ctx.lookup(queueName);
          · 用 ConnectionFactory 創建Connection
          qcon = qconFactory.createQueueConnection();
          · 用 Connection 創建一個Session
          qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
          · 用 Session 和 Destination 創建MessageConsumer
          qreceiver = qsession.createReceiver(queue);
          · 設置監聽
          qreceiver.setMessageListener(this);
          · 啟動 Connection
          qcon.start();
          3. 源代碼
          package jmssample;

          import java.util.Hashtable;
          import javax.jms.*;
          import javax.naming.Context;
          import javax.naming.InitialContext;
          import javax.naming.NamingException;
          import java.util.Hashtable;
          import javax.jms.*;
          import javax.naming.Context;
          import javax.naming.InitialContext;
          import javax.naming.NamingException;

          /**
          * This example shows how to establish a connection to
          * and receive messages from a JMS queue. The classes in this
          * package operate on the same JMS queue. Run the classes together to
          * witness messages being sent and received, and to browse the queue
          * for messages. This class is used to receive and remove messages
          * from the queue.
          *
          * @author Copyright (c) 1999-2003 by BEA Systems, Inc. All Rights Reserved.
          */
          public class QueueReceive implements MessageListener
          {
          // Defines the JNDI context factory.
          public final static String JNDI_FACTORY="weblogic.jndi.WLInitialContextFactory";

          // Defines the JNDI provider url.
          public final static String PROVIDER_URL=" t3://localhost:80";

          // Defines the JMS connection factory for the queue.
          public final static String JMS_FACTORY="SendJMSFactory";

          // Defines the queue.
          public final static String QUEUE="SendJMSQueue";

          private QueueConnectionFactory qconFactory;
          private QueueConnection qcon;
          private QueueSession qsession;
          private QueueReceiver qreceiver;
          private Queue queue;
          private boolean quit = false;

          /**
          * Message listener interface.
          * @param msg message
          */
          public void onMessage(Message msg)
          {
          try {
          String msgText;
          if (msg instanceof TextMessage) {
          msgText = ((TextMessage)msg).getText();
          } else {
          msgText = msg.toString();
          }

          System.out.println("Message Received: "+ msgText );

          if (msgText.equalsIgnoreCase("quit")) {
          synchronized(this) {
          quit = true;
          this.notifyAll(); // Notify main thread to quit
          }
          }
          } catch (JMSException jmse) {
          jmse.printStackTrace();
          }
          }

          /**
          * Creates all the necessary objects for receiving
          * messages from a JMS queue.
          *
          * @param ctx JNDI initial context
          * @param queueName name of queue
          * @exception NamingException if operation cannot be performed
          * @exception JMSException if JMS fails to initialize due to internal error
          */
          public void init(Context ctx, String queueName)
          throws NamingException, JMSException
          {
          qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
          qcon = qconFactory.createQueueConnection();
          qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
          queue = (Queue) ctx.lookup(queueName);
          qreceiver = qsession.createReceiver(queue);
          qreceiver.setMessageListener(this);
          qcon.start();
          }

          /**
          * Closes JMS objects.
          * @exception JMSException if JMS fails to close objects due to internal error
          */
          public void close()throws JMSException
          {
          qreceiver.close();
          qsession.close();
          qcon.close();
          }
          /**
          * main() method.
          *
          * @param args WebLogic Server URL
          * @exception Exception if execution fails
          */

          public static void main(String[] args) throws Exception {

          InitialContext ic = getInitialContext();
          QueueReceive qr = new QueueReceive();
          qr.init(ic, QUEUE);

          System.out.println("JMS Ready To Receive Messages (To quit, send a \"quit\" message).");

          // Wait until a "quit" message has been received.
          synchronized(qr) {
          while (! qr.quit) {
          try {
          qr.wait();
          } catch (InterruptedException ie) {}
          }
          }
          qr.close();
          }

          private static InitialContext getInitialContext()
          throws NamingException
          {
          Hashtable env = new Hashtable();
          env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
          env.put(Context.PROVIDER_URL, PROVIDER_URL);
          return new InitialContext(env);
          }


          }
          七. 測試消息發送和接收
          1. 設置WebLogic Classpath;


          2. 轉到發送接收程序目錄編譯文件;

          3. 執行接受程序;
          4. 打開另一窗口,執行發送程序;


          5. 輸入發送消息"quit",接收程序結束.


          總結
          本文先簡要介紹了JMS的一些基本概念,繼而引入了WebLogic JMS Server的體系結構和相關特性。在此基礎之上,圖文并茂地講述了JMS在WebLogic Server 8.1上的配置。最后在解剖JMS應用程序框架的同時,以點對點為例演示了JMS的發送接收消息流程。

          ?作者簡介
          周海根是(dev2dev論壇id:zhouhg) 長城軟件系統有限公司 J2EE架構設計師, 項目經理
          posted @ 2006-04-13 09:00 小小程序程序員混口飯吃 閱讀(563) | 評論 (0)編輯 收藏
          ?
          在Struts中reset方法有什么作用(轉)
          大大熊 發表于 2005-6-7 17:48:00

           

          ?
          創建人:王藝
          創建時間:2003年6月15日星期日


          第一步:
          對象的可視范圍:request、session、application、page。
          Request:在一個請求周期內有效。就是從你點擊頁面上的一個按鈕開始到服務器返回響應頁面為止(包括響應頁面)。
          Session:在一個用戶與服務器建立連接的整個過程中有效。
          Application:在整個web應用程序內有效。
          Page:僅在一個jsp頁面內有效。

          第二步:
          ActionForm在你確定的有效期(可視范圍)內是唯一的。

          第三步:
          在每次為ActionForm賦值前調用它的reset方法。作用是使ActionForm中的值恢復初始狀態。在應用中我們可以通過在reset中為變量賦初值的方式,使得頁面上的某個對象有顯示值。

          第四步:
          可視范圍與賦值前的初始化結合。
          由于第二步所述特性,如果可視范圍是request,則reset方法并不是很重要,因為你每次調用時都會產生一個新的ActionForm實例,所以你所操作的ActionForm不會與別人分享同時也就不會受別人的影響;如果可視范圍是session,由于在session范圍內此ActionForm是唯一的,所以你在session范圍內需要用到此ActionForm的地方調用的都是同一個ActionForm,要是你沒有在reset中對變量賦初值那么前一次調用ActionForm是為它賦的值將在此次調用時有效,這到也沒什么。但是,如果恰巧再次調用時你僅僅需要為ActionForm中的一部分變量賦值,那么其余的變量將保持上一次得到的值,這樣你就得到了一個“新舊混合體”,我想這多半不是你所期望的;如果可視范圍是application,那其影響就更是不難理解了,這時不但是你自己會影響你自己,使用應用的其他用戶的操作也會影響到你。

          第五步:
          知道了reset方法的作用和ActionForm在scope內唯一的特性后就為我們靈活處理ActionForm的行為提供了基礎。比如說你現在需要跨過多個頁面收集數據信息,這時你就可以把scope設置為session,并且不實現reset方法――這樣在每個頁面put數據時都不會將之前收集的數據清空,最后在你收集完數據后在Action中調用ActionForm中你自定義的初始化方法,如:resetField。
          在具體的我也想不出了,還是要大家在應用時多多體會這些特性,這樣才能把架構的威力發揮到最大。

          posted @ 2006-04-13 09:00 小小程序程序員混口飯吃 閱讀(1752) | 評論 (0)編輯 收藏
          僅列出標題
          共11頁: First 上一頁 3 4 5 6 7 8 9 10 11 下一頁 
          主站蜘蛛池模板: 商丘市| 仙居县| 南澳县| 滁州市| 增城市| 吴忠市| 莱州市| 梨树县| 南投县| 宁武县| 丰台区| 尼玛县| 南乐县| 台湾省| 日喀则市| 兖州市| 彰武县| 荔波县| 衢州市| 尉犁县| 宁化县| 永善县| 星子县| 紫金县| 鹤庆县| 莱西市| 德化县| 永城市| 古丈县| 乐业县| 晴隆县| 电白县| 田东县| 武功县| 泾川县| 双牌县| 九江市| 馆陶县| 达州市| 望城县| 溧水县|