開發呼叫 EJB 非同步方法的用戶端程式碼

您可以使用這個主題內的範例程式碼來開發呼叫 EJB 非同步方法的用戶端程式碼。

開始之前

這項作業假設下列介面和 Bean 實作類別存在:
public interface AcmeRemoteInterface {
   void fireAndForgetMethod ();
   Future<Integer> methodWithResults() throws AcmeException1, AcmeException2;
}
@Stateless
@Remote(AcmeRemoteInterface.class)
@Asynchronous
public class AcmeAsyncBean {
   public void fireAndForgetMethod () {
      // do non-critical work
   }

   public Integer methodWithResults() {
      Integer result;
      // do work, and then return results
      return result;
   }
}

關於這項作業

程序

  • 建立用戶端程式碼,以呼叫未傳回任何結果的非同步方法,有時稱為 fire and 遺忘 方法。 這種非同步方法無法導致應用程式異常狀況。 不過,可能會發生必須解決的系統異常狀況。
    
    @EJB AcmeRemoteInterface myAsyncBean;
    
    try {
       myAsyncBean.fireAndForgetMethod();
    } catch (EJBException ejbex) {
       // Asynchronous method never dispatched, handle system error
    }
  • 建立用戶端程式碼,以呼叫傳回結果的非同步方法。
    • 建立用戶端程式碼來呼叫非同步方法,其中用戶端會等待最多 5 秒 (在這個時間範圍期間,會封鎖用戶端執行緒) 來接收結果。 異常狀況處理需求與前一個步驟相同。 例如:
      myResult = myFutureResult.get(5, TimeUnit.SECONDS);
      
    • 建立用戶端程式碼,以呼叫無法立即取得結果的非同步方法。 執行方法之後,用戶端會擷取結果。 此方法可防止用戶端執行緒封鎖,而且在輪詢結果時,用戶端可以自由執行其他工作。 異常狀況處理需求與先前步驟相同。 In this example, the client periodically polls the Future<V> object to determine when the asynchronous method has finished executing. 例如:
      while (!myFutureResult.isDone()) {
      // Execute other work while waiting for the asynchronous method to complete.
      }
      
      // This call is guaranteed not to block because isDone returned true.
      myResult = myFutureResult.get(); 
  • 建立用戶端程式碼以處理應用程式異常狀況。
    The client code calls an asynchronous method which returns an application exception in the Future<V> object. 下列範例示範判斷發生哪一個應用程式異常狀況所需的異常狀況處理程式碼。
    
    @EJB AcmeRemoteInterface myAsyncBean;
    
    Future<Integer>>myFutureResult = null;
    Integer myResult = null;
    
    try {
       myFutureResult = myAsyncBean.methodWithResults();
    } catch (EJBException ejbx) {
       // Asynchronous method never dispatched, handle exception
    }
    
    // Method is eventually dispatched.  Wait for results.
    
    try {
       myResult = myFutureResult.get();
    } catch (ExecutionException ex) {
       // Determine which application exception that occurred during the
       // asynchronous method call.
       Throwable theCause = ex.getCause();
       if (theCause instanceof AcmeException1) {
          // Handle AcmeException1
       } else if (theCause instanceof AcmeException2) {
          // Handle AcmeException2
       } else {
          // Handle other causes.
       }
    } catch ( ... ) {
       // Handle other exception.
    }
  • 建立用戶端程式碼,以識別非同步方法呼叫在執行期間所擲出的系統異常狀況。
    下列範例示範判斷是否發生系統異常狀況所需的異常狀況處理程式碼。
    
    @EJB AcmeRemoteInterface myAsyncBean;
    
    Future<Integer>>myFutureResult = null;
    Integer myResult = null;
    
    try {
       myFutureResult = myAsyncBean.methodWithResults();
    } catch (EJBException ejbx) {
       // Asynchronous method was not dispatched; handle exception.
    }
    
    // Method will eventually be dispatched so block now and wait for results
    
    try {
       myResult = myFutureResult.get();
    } catch (ExecutionException ex) {
       // Find the exception class that occurred during the asynchronous method
       Throwable theCause = ex.getCause();
       if (theCause instanceof EJBException) {
          // Handle the EJBException that might be wrapping a system exception
          // which occurred during the asynchronous method execution.
          Throwable theRootCause = theCause.getCause();
          if (theRootCause != null) {
             // Handle the system exception
          }
       } else ... // handle other causes 
    } catch (...) {
       // handle other exceptions
    }
  • 選擇性地建立用戶端程式碼來取消非同步方法呼叫。 如果此嘗試成功,則 Future.isCancelled 方法會傳回 true , Future.get 方法會導致 CancellationException。
    下列範例示範取消非同步方法呼叫所需的程式碼。
    @EJB AcmeRemoteInterface myAsyncBean;
    
    Future<Integer> myFutureResult = myFutureResult.methodWithResults();
    Integer myResult;
    
    if (myFutureResult.cancel(true)) {
       // Asynchronous method was not dispatched.
    } else {
       // Asynchronous method already started executing.  The bean can still check
       // whether an attempt was made to cancel the call.
    }
    
    if (myFutureResult.isCancelled()) {
       // Asynchronous method call did not start executing because the cancel
       // method returned true in a previous line of the example.
    }
    
    try {
       myResult = myFutureResult.get();
    } catch (CancellationException ex) {
       // Handle the exception that occurs because the cancel method returned true
       // in a previous line of the example.
    }
  • 選擇性地建立 Bean 程式碼,以檢查用戶端是否嘗試取消非同步方法呼叫。
    下列範例示範檢查用戶端是否嘗試取消非同步方法呼叫所需的 Bean 程式碼。 如果用戶端嘗試取消工作,則 SessionContext.wasCancelCalled 方法會傳回 true , Bean 程式碼可以避免不必要的工作。
    @Resource SessionContext myContext;
    
    public Future<Integer> methodWithResults() {
       for (int i = 0; i < 3; i++) {
          // Do one piece of long-running work.
    
          // Before continuing, check whether the client attempted to cancel this work.
          if (myContext.wasCancelCalled()) {
             throw new AcmeCancelCalledException();
          }
       }
    
       // ...
    }
  • 從非同步方法呼叫傳回多個輸出值。

    在某些情況下,方法必須傳回多個資料片段。

    完成此作業的方法之一是使用「依參照傳遞」語意。 在此方法中,物件會以參數形式傳遞至方法,並由方法更新,然後更新的值可供用戶端使用。 此方法適用於非同步方法,但它不是最佳型樣。

    若要傳回多個資料片段,請在方法所傳回的 Future 物件內建立封套。 在此方法中,會定義封套物件,其中包含保留必須傳回之不同資料片段的實例變數。 非同步方法會將這些資料片段設為封套物件並傳回它,然後用戶端程式碼會從 Future 物件擷取此資料。

    在封套物件內內嵌多個資料片段是本端或遠端透通型樣,可確切識別何時可以使用結果。 相反地,傳統的按參照傳遞技術無法讓客戶輕鬆判斷何時可以取得結果。 在非同步方法執行之前,不會更新傳入的物件,用戶端無法判斷發生的時間,除了使用 Future.isDone () 或 Future.get () 方法來詢問 Future 物件之外。

    
    // This is the result object that is returned from the asynchronous method.
    // This object is wrappered in a Future object, and it contains the two pieces of data
    // that must be returned from the method.
    class ResultObject {
         public Boolean myResult; 
         pubilc String myInfo;
    } 
                        
    
    // This is the asynchronous method code that gets the results and returns them.
    @Asynchronous
    public Future<ResultObject> asyncMethod1(Object someInputData) {
         boolean result = doSomeStuff();
         String info = doSomeMoreStuff();
         ResultObject theResult = new ResultObject();
         theResult.myResult = result;
         theResult.myInfo = info;
         return new javax.ejb.AsyncResult<ResultObject>(theResult);     
    }
                        
    
    // This is the client code that obtains the ResultObject, and then extracts the needed data from it.
    Future<ResultObject> myFutureResult = myBeanRef.asyncMethod1(someInputData);
    ResultObject theResult = myFutureResult.get();
    boolean didItWork = theResult.myResult;
    String explanation = theResult.myInfo;