この章では、Oracle Enterprise Schedulerを使用してEnterprise Java Bean (EJB)ジョブを作成する方法について説明します。
この章の構成は、次のとおりです。
EJBジョブ・タイプを使用するとJavaベースのジョブを作成でき、事前デプロイ済のホスティング・アプリケーションの利便性を利用できます。さらに、EJBは別のサーバー上にリモートで配置できます。
Java SEベースのジョブと異なり、EJBジョブはホスティング・アプリケーションの内部に埋め込む必要がありません。このため、リモートで配置でき、事前デプロイ済のホスティング・アプリケーションで使用できます。EJBジョブは、事前デプロイ済のホスティング・アプリケーションを含むどのホスティング・アプリケーションからも起動できます。EJBの実装は、スケジューラ・サーバーと同じWebLogicドメイン内にある必要があることに注意してください。
EJBは、Oracle Enterprise Scheduler (共有ライブラリ内で定義)で定義されているインタフェースに準拠しています。EJBは非トランザクションです。同期および非同期の両方のEJBジョブがサポートされます。非同期で実行するとEJBは即座に戻り、Oracle Enterprise Scheduler EJBは後でジョブの完了時に起動されます。EJB実装は、3つの共有ライブラリ・サーバー(oracle.ess
)、クライアント(oracle.ess.client
)およびシン・クライアント(oracle.ess.thin.client
)のいずれとも動作可能です。シン・クライアント共有ライブラリは、Oracle Enterprise Schedulerデータ・ソースに依存しません。シン・クライアント・ライブラリの使用の詳細は、「シン・クライアント・アプリケーションの作成」を参照してください。
EJBはサブリクエストを発行でき、出力およびログに書き込むことができます。EJBインタフェースは、Javaジョブに類似しているか同じであり、類似の処理を実行できます。パフォーマンスを向上させるために、単一の共有EJBで複数のジョブ実装を統合できます。
EJBジョブは、EJBリモート・ビジネス・インタフェースを使用してリモートで実行されるJavaジョブです。実行タイプはJAVA_TYPE
です。リモート・ジョブとは、リモート・サーバーにデプロイされているEJBです。このBeanのリモート・ビジネス・インタフェースは、oracle.as.scheduler.RemoteExecutable
インタフェースを拡張し、execute
メソッドを定義します。Oracle Enterprise Schedulerとサービス・コンポーネント間の契約は、execute
メソッドで定義されます。図12-1は、標準的なEJBジョブ・デプロイメント内のコンポーネントを示しています。
EJBは、ホスティング・アプリケーションと同じドメインに配置する必要があり、Subject
オブジェクトはEJBに伝播されます。JNDIルックアップ操作で、オプションの資格証明を指定できます。デフォルト・アイデンティティは、anonymousです。
Oracle Enterprise Schedulerは柔軟であり、実装およびデプロイメント・オプションが用意されています。「ジョブ開発の計画」は、ジョブ開発およびデプロイメント・プロセスの計画方法の概要を説明しています。
Oracle Enterprise SchedulerでEJBタイプのジョブを使用するには、メタデータ・サービスを特定してジョブ定義を作成する必要があります。ジョブ定義は、名前とジョブ・タイプを指定して作成します。ジョブ定義を作成するときには、特定のシステム・プロパティも設定する必要があります。
ジョブ定義は、メタデータ・サービスを使用してメタデータ・リポジトリに格納できます。サンプル・メタデータ・ファイルは、この章の後半で説明します。
メタデータ・サービスの使用方法の詳細は、「メタデータ・サービスの使用」を参照してください。
ジョブ定義のJobType
を指定する場合、JobType
に関連付けられる特性を定義するSystemProperties
を指定することもできます。表12-1および表12-2に、リクエストの処理方法を指定するプロパティを示します。
表12-1 EJBジョブ・タイプのプロパティ
プロパティ名[SystemPropertyクラスのフィールド] | 説明 |
---|---|
|
オプションです。リモート・サーバーのURLを指定します。EJBがリモートに配置されている場合にのみ必須です。
次に2つの例を示します。
|
|
必須です。リモートEJB実装のJNDIルックアップ名を指定します。 例: |
|
オプションです。適切なビジネス・メソッドに分岐するEJBの実装で使用されるパス・スルー・パラメータを指定します。 例: |
|
EJBサーバーのJNDIツリーが保護されている場合のみ必須です。キーストア内のユーザー名とパスワードにマップされ、CSFの別名を指します。この特定のユーザー名/パスワードのペアは、 このプロパティは、 |
注意:
Oracle Enterprise Manager Fusion Middleware ControlまたはWLSTスクリプトを使用して、CSFキー別名をインストール後の手順として構成できます。インストール後の手順の前に、キーストアのCSFマップをoracle.ess.security
のデフォルト値に設定できます。
表12-2に、RequestParameters
オブジェクト、またはホスティング・アプリケーションのOracle Enterprise Scheduler構成のいずれかに追加できるプロパティを示します。テスト環境から本番環境に移行する際ジョブ定義はレプリケートされるため、本番環境で環境固有のデータをジョブ定義に入力しないでください。かわりに、このデータは、構成データとしてホスティング・アプリケーションで個別に入力する必要があります。Oracle Enterprise Schedulerのトークン置換および論理クラスタ機能では、T2Pプロセス中にターゲット・デプロイメントに正しく適合するよう簡単に変更できるようにメタデータを抽象化できます。これらの機能の使用の詳細は、「トークンおよび論理クラスタの使用」を参照してください。
表12-2 追加プロパティ
プロパティ名[SystemPropertyクラスのフィールド] | 説明 |
---|---|
|
オプションです。認証済Oracle Enterprise SchedulerサーバーのCSFキーの別名を指定します。このプロパティは、Oracle Enterprise SchedulerのJNDIツリーが認証済の場合にのみ必須です。 例: |
|
ホスティング・アプリケーションで定義され、Oracle Enterprise SchedulerサーバーのJNDIツリーにバインドされる、Oracle Enterprise Scheduler このプロパティは、 |
|
ホスティング・アプリケーションで定義され、Oracle Enterprise SchedulerサーバーのJNDIツリーにバインドされる、Oracle Enterprise Scheduler
|
|
ホスティング・アプリケーションで定義され、Oracle Enterprise SchedulerサーバーのJNDIツリーにバインドされる、Oracle Enterprise Scheduler このプロパティは、次の場合にのみ必須です。
|
システム・プロパティの詳細は、「パラメータとシステム・プロパティの使用」を参照してください。
リモートEJBの保護された起動は、そのサーバーのJNDIツリーが認証されている場合に必須です。これは、保護された参照を使用してリモートEJBがOracle Enterprise Scheduler EJBをコールバックする場合も同様です。次の各項で、ガイドラインを示します。
次の内容が転送起動に適用されます。
Oracle Enterprise SchedulerがリモートEJBを起動する場合、実行ジョブのサブジェクトは常に伝播されます。
Oracle Enterprise Schedulerがジョブを実行する際、現在のOracle Enterprise Scheduler ServerのJndiProviderUrl
はRequestParameters
を介してリモートEJBに常に提供されます。
リモート・サーバーのJNDIツリーが認証されている場合、JNDI_CSF_KEY
プロパティをリクエスト・パラメータまたはホスティング・アプリケーションのEssConfiguration
で指定する必要があります。
Oracle Enterprise Schedulerは、CsfKey
のキーストアを参照してPasswordCredential
を取得し、リモート・サーバーに接続します。
次の内容がコールバック起動に適用されます。
リモートEJBがOracle Enterprise SchedulerのBeanにコールバックする必要がある場合、次のプロパティを指定できます。
HostingApp
で公開されているOracle Enterprise SchedulerのRuntime
、Metadata
およびAsyncRequest
BeanのJNDI名は、リクエスト・パラメータまたはホスティング・アプリケーションのEssConfiguration
で指定する必要があります。EssNativeHostingApp
が使用されている場合、これらのエントリは必要がありません。
Oracle Enterprise SchedulerサーバーのJNDIツリーが認証されている場合、ESS_JNDI_CSF_KEY_NAME
プロパティをリクエスト・パラメータまたはホスティング・アプリケーションのEssConfiguration
で指定する必要があります。Oracle Enterprise Schedulerにより、このプロパティがRequestParameters
を介してリモートEJBで使用可能になります。
リモートEJBは、RemoteConnector
APIを使用して、 Oracle Enterprise Scheduler Beanのリモート・インスタンスを取得できます。これは、次を渡すことで実行できます。
RequestParameters
BeanのRequestParameters
およびJndiMappedName
(ネイティブ・ホスティング・アプリケーション以外のホスティング・アプリケーション用)
RequestParameters
、ユーザー名およびパスワード(Oracle Enterprise Schedulerサーバーが認証されている場合)
InitialContext
(主にEssNativeHostingApp
を伴うJava SEクライアント用)
InitialContext
およびjndiMappedName
(主にその他のホスティング・アプリケーションを伴うJava SEクライアント用)
コードの実装がOracle Enterprise Scheduler EJBへのアクセスに依存している場合、RemoteConnector
APIクラスで公開されているメソッドを使用します。Oracle Enterprise Schedulerでは、JNDIルックアップを実行する前にサーバー・アフィニティ・プロパティをInitialContext
環境で設定する必要があり、RemoteConnector
APIクラスがこのプロパティを設定します。これは、マルチノード・クラスタ・シナリオで特に重要です。RemoteConnector
クラスは、Oracle Enterprise Schedulerクライアント・ライブラリでパッケージ化されています。
何らかの理由でRemoteConector
クラスを使用できない場合、次の例に示すように、Oracle Enterprise Scheduler EJBの参照を実行する前に環境マップ・プロパティをInitialContext
に設定できます。
if(PlatformUtils.isWebLogic()) envProps.put("weblogic.jndi.enableServerAffinity", "true");
マルチノード・クラスタ環境では、クラスタ・アルゴリズムをラウンドロビン・アフィニティに設定するのが最適です。
Oracle Enterprise SchedulerサービスのBeanが認証されている場合、リモート・アプリケーションは、保護された参照を使用してOracle Enterprise Schedulerにコールバックする必要があります。リクエスト・パラメータで使用可能なESS_JNDI_CSF_KEY_NAME
プロパティを使用するOracle Enterprise SchedulerのRemoteConnector
APIを使用して、参照を実行できます。ただし、このCSF参照を支援するために、RemoteConnector
を起動するコードは資格証明ストア・アクセスの権限を付与する必要があります。次のXMLの一部分を、リモート・アプリケーションのjazn-data.xml
ファイルに追加できます。
<jazn-policy> <grant> <grantee> <codesource> <url>file:${domain.home}/servers/${weblogic.Name}/tmp/ _WL_user/<AppName>/-</url> </codesource> </grantee> <permissions> <permission> <class>oracle.security.jps.service.credstore.CredentialAccessPermission </class> <name>context=SYSTEM,mapName=oracle.ess.security,keyName=*</name> <actions>READ</actions> </permission> <permission> <class>oracle.security.jps.JpsPermission</class> <name>IdentityAssertion</name> <actions>execute</actions> </permission> <permission> <class>oracle.security.jps.JpsPermission</class> <name>AppSecurityContext.setApplicationID.*</name> </permission> </permissions> </grant> </jazn-policy>
この項では、同期Beanの作成方法を表す例を示します。
この項では、同期Beanに適用されるメタデータを示します。
次の例で、ファイルoracle/apps/ess/custom/Jobs/EssGatewayJobDefn.xml
にあるEJBジョブのジョブ定義のサンプルを示します
<?xml version = '1.0'?>
<job-definition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/scheduler"
name="SoaEjbJobDefn"
job-type="/oracle/as/ess/core/JobType/SyncEjbJobType.xml">
<description/>
<display-name>EssGatewayBean</display-name>
<parameter-list>
<parameter name="SYS_EXT_jndiKeyName" data-type="string" read-only="true">
ejb/essGatewayBean</parameter>
<parameter name="SYS_EXT_jndiProviderUrl" data-type="string" read-only="true">URL</parameter>
<parameter name="SYS_EXT_ejbOperationName" data-type="string"
read-only="true">activateFileAdapter</parameter>
<parameter name="SYS_effectiveApplication" data-type="string">
ESS_NATIVE_HOSTING_APP_LOGICAL_NAME</parameter>
</parameter-list>
</job-definition>
この項では、Oracle Enterprise Schedulerで予期される同期EJBジョブのサンプル実装を示します。
次のコード・スニペットは、このBeanを定義するejb-jar.xml
ファイルの一部を示しています。
<session> <description>ESS Gateway Bean</description> <ejb-name>EssGateway</ejb-name> <ejb-class>com.soa.beans.EssGatewayBean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> <security-identity> <use-caller-identity/> </security-identity> </session>
次のコード・スニペットは、このBeanを定義するweblogic-ejb-jar.xml
ファイルの一部を示しています。
<weblogic-enterprise-bean> <ejb-name>FileAdapterBean</ejb-name> <stateless-session-descriptor> <business-interface-jndi-name-map> <business-remote>oracle.as.scheduler.RemoteCancellableExecutable </business-remote> <jndi-name>ejb/essGatewayBean</jndi-name> </business-interface-jndi-name-map> </stateless-session-descriptor> </weblogic-enterprise-bean>
import javax.ejb.Stateless; import oracle.as.scheduler.SystemProperty; import oracle.as.scheduler.ExecutionCancelledException; import oracle.as.scheduler.ExecutionErrorException; import oracle.as.scheduler.ExecutionPausedException; import oracle.as.scheduler.ExecutionWarningException; import oracle.as.scheduler.RemoteCancellableExecutable; import oracle.as.scheduler.RequestExecutionContext; import oracle.as.scheduler.RequestParameters; @Stateless(name = "EssGateway", mappedName = "ejb/essGatewayBean") public class EssGatewayBean implements RemoteCancellableExecutable { public EssGatewayBean() { } public void execute(RequestExecutionContext context, RequestParameters parameters) throws ExecutionErrorException, ExecutionWarningException, ExecutionCancelledException, ExecutionPausedException { //Get the value of 'SYS_EXT_ejbOperationName' property String opName = (String)parameters.getValue(SystemProperty.EJB_OPERATION_NAME); if("manageFileAdapter".equals(opName)) { // //Call business method of this bean or some other bean // //Hint: User defined properties can be set in RequestParameters while //submitting the job and can be retrieved here for further processing. } } public void cancel(RequestExecutionContext context, RequestParameters parameters) { // //Logic to cancel the execution of a business method. // // Execute the actual logic of cancellation, notifies back to ESS // by throwing ExecutionCancelledException through execute method. } }
Oracle Enterprise SchedulerがBeanを非同期に起動する際、実行メソッドの終了を待機しません。このため、Beanの実装は処理が終了した後にOracle Enterprise Schedulerに通知する必要があります。この目的のためにRemoteAsyncHelper
クラスを使用できます。または、RemoteConnector
から取得されるAsyncRequestBean
を使用して、ステータスの更新をOracle Enterprise Schedulerに通知できます。
通常、非同期EJBは次の用途に使用されます。
長時間実行されている操作
プロセッサを大量に消費するタスク
バックグラウンド・タスク
アプリケーション・スループットを向上させる場合
メソッドの起動結果が即座に必要ではないときにアプリケーション・レスポンス時間を改善する場合
同期EJBジョブは、実行時間が短いユーザー・ビジネス・メソッドにより適しています。
Oracle Enterprise SchedulerがBeanを非同期で実行するにはいくつかの方法があります。
明示的非同期性: 同期ステートレスBeanを使用して、メッセージドリブンBeanを非同期で起動します。(Java Message Serviceメッセージを、メッセージドリブンBeanによってリスニングされているトピック/キューに追加します)
暗黙的非同期性: 非同期で動作するように、EJB非同期注釈を使用してビジネス・メソッド(実行、取消しメソッド以外)を宣言します。
注意:
Oracle Enterprise Schedulerは、同期Beanを非同期で起動できます。ただし、このメソッドを使用する場合、長時間実行メソッドが非同期性のためにマークされる方法でBeanをモデル化する必要があります。
注意:
EJB標準で定義されているように、executeメソッドは許可されていないカスタム例外をスローするため、execute
メソッドの@Asynchronous
注釈またはクラス全体を使用できません。Oracle Enterprise Schedulerでは、カスタム例外をスローするためにexecute
メソッドが必要です。
この項では、非同期Beanに適用されるメタデータを示します。
この例では、ファイルoracle/apps/ess/custom/Jobs/AsyncJobDefn.xml
にあるEJBジョブのジョブ定義のサンプルを示します
<?xml version = '1.0'?> <job-definition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/scheduler" name="EssAsyncJobDefn" job-type="/oracle/as/ess/core/JobType/AsyncEjbJobType.xml"> <display-name>EssGatewayBean</display-name> <parameter-list> <parameter name="SYS_EXT_jndiKeyName" data-type="string" read-only="true">ejb/essAsyncGatewayBean</parameter> <parameter name="SYS_EXT_jndiProviderUrl" data-type="string" read-only="true">t3://localhost:10801</parameter> <parameter name="SYS_EXT_ejbOperationName" data-type="string"read-only="true">activateFileAdapter</parameter> <parameter name="SYS_effectiveApplication" data-type="string"> ESS_NATIVE_HOSTING_APP_LOGICAL_NAME</parameter> </parameter-list> </job-definition>
この項では、「非同期Bean」で説明している明示的および暗黙的メソッドの両方を使用して非同期性を実装する方法を表すサンプル・コードを示します。
次のコード・サンプルでは、メッセージドリブンBeanを非同期で起動するために使用される同期ステートレスBeanを示します。
package com.soa.test; import java.io.Serializable; import java.util.ArrayList; import javax.ejb.Stateless; import javax.jms.ObjectMessage; import javax.jms.Queue; import javax.jms.QueueConnection; import javax.jms.QueueConnectionFactory; import javax.jms.QueueSender; import javax.jms.QueueSession; import javax.jms.Session; import javax.naming.InitialContext; import oracle.as.scheduler.AsyncRequestBeanRemote; import oracle.as.scheduler.ExecutionCancelledException; import oracle.as.scheduler.ExecutionErrorException; import oracle.as.scheduler.ExecutionPausedException; import oracle.as.scheduler.ExecutionWarningException; import oracle.as.scheduler.RemoteCancellableExecutable; import oracle.as.scheduler.RequestExecutionContext; import oracle.as.scheduler.RequestParameters; import oracle.as.scheduler.request.RemoteConnector; @Stateless(name = "EssAsyncPilot") public class EssAsyncPilotBean implements RemoteCancellableExecutable { public EssAsyncPilotBean() { } public void execute(RequestExecutionContext requestExecutionContext, RequestParameters requestParameters) throws ExecutionErrorException, ExecutionWarningException, ExecutionPausedException, ExecutionCancelledException { // Delegate the job request cancellation to message driven bean postToQueue("execute", requestExecutionContext, requestParameters); } public void cancel(RequestExecutionContext requestExecutionContext, RequestParameters requestParameters) { RemoteConnector connector = new RemoteConnector(); AsyncRequestBeanRemote asyncRequest; // Delegate the job request cancellation to message driven bean try { postToQueue("cancel", requestExecutionContext, requestParameters); } catch (Exception e) { //Mark this request as ERRORed } /* * Other ways to cancel the job request. * asyncRequest = connector.getAsyncRequestEJB(requestParameters); * asyncRequest.onCancel(requestExecutionContext); * * (or) * * asyncRequest = connector.getAsyncRequestEJB(requestParameters); * asyncRequest.setRequestStatus( * requestExecutionContext, AsyncStatus.CANCEL, "Cancelling the job"); * * (or simply) * * RemoteAsyncHelper asyncHelper = new RemoteAsyncHelper( * requestExecutionContext, requestParameters); * asyncHelper.onCancel(); * */ } private void postToQueue(String instruction, RequestExecutionContext context, RequestParameters params) { try { QueueConnectionFactory qconFactory; QueueConnection qcon; QueueSession qsession; QueueSender qsender; Queue queue; ObjectMessage msg; InitialContext ic = new InitialContext(); qconFactory = (QueueConnectionFactory) ic .lookup("EssAsyncJmsConnFactory"); qcon = qconFactory.createQueueConnection(); qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); queue = (Queue) ic.lookup("EssAsyncJmsQueue"); qsender = qsession.createSender(queue); msg = qsession.createObjectMessage(); qcon.start(); ArrayList<Serializable> objsList = new ArrayList<Serializable>(2); objsList.add(context); objsList.add(params); objsList.add(instruction); msg.setObject(objsList); qsender.send(msg); System.out.println("The message, " + instruction + ", has been sent to the EssAsyncJmsQueue."); qsender.close(); qsession.close(); qcon.close(); } catch (Exception e) { System.out.print("error " + e); } } }
import java.util.List; import java.io.Serializable; import javax.ejb.MessageDriven; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.ObjectMessage; import oracle.as.scheduler.RequestExecutionContext; import oracle.as.scheduler.RequestParameters; import oracle.as.scheduler.async.RemoteAsyncHelper; /** * This message driven bean sample relies on execute/cancel instructions. * Upon completion of execution or cancellation, this bean notifies * ESS about its status so that the job request is marked for completion. */ @MessageDriven(mappedName = "ejb/essAsyncJms") public class EssAsyncJmsBean implements MessageListener { public void onMessage(Message message) { if (message instanceof ObjectMessage) { ObjectMessage objMessage = (ObjectMessage)message; try { List<Serializable> objsList = (List<Serializable>)objMessage.getObject(); RequestExecutionContext ctx = (RequestExecutionContext)objsList.get(0); RequestParameters params = (RequestParameters)objsList.get(1); String instruction = (String)objsList.get(2); RemoteAsyncHelper asyncHelper = new RemoteAsyncHelper(ctx, params); if ("cancel".equalsIgnoreCase(instruction)) { //EssAsyncJmsBean.cancel: Cancelling the Execution try { //Do the actual cancellation Thread.sleep(1000); } catch (InterruptedException e) { } asyncHelper.onCancel(); //EssAsyncJmsBean.cancel: Completed cancellation } else { //EssAsyncJmsBean.execute: Started the Execution "); try { //Do the actual execution Thread.sleep(5000); } catch (InterruptedException e) { } asyncHelper.onSuccess(); //EssAsyncJmsBean.execute: Completed the Execution "); } } catch (Exception e) { e.printStackTrace(); } } } }
次のコード・スニペットでは、EJB Asynchronous
を使用して、Beanまたはそのメソッドが非同期に動作するように宣言します。
package com.soa.test; import java.util.concurrent.Future; import javax.annotation.Resource; import javax.ejb.AsyncResult; import javax.ejb.Asynchronous; import javax.ejb.SessionContext; import javax.ejb.Stateless; import javax.xml.transform.Result; import oracle.as.scheduler.ExecutionCancelledException; import oracle.as.scheduler.ExecutionErrorException; import oracle.as.scheduler.ExecutionPausedException; import oracle.as.scheduler.ExecutionWarningException; import oracle.as.scheduler.RemoteExecutable; import oracle.as.scheduler.RequestExecutionContext; import oracle.as.scheduler.RequestNotFoundException; import oracle.as.scheduler.RequestParameters; import oracle.as.scheduler.RuntimeServiceException; import oracle.as.scheduler.SchedulerException; import oracle.as.scheduler.async.RemoteAsyncHelper; @Stateless(name = "EssAsyncAnnotatedBean", mappedName = "ejb/essAsyncAnnBean") public class EssAsyncAnnotatedBean implements RemoteExecutable { @Resource SessionContext sessionContext; public void execute(RequestExecutionContext requestExecutionContext, RequestParameters requestParameters) throws ExecutionErrorException, ExecutionWarningException, ExecutionPausedException,ExecutionCancelledException { RemoteAsyncHelper asyncHelper = null; try { asyncHelper = new RemoteAsyncHelper(requestExecutionContext, requestParameters); //Initiate processing initiateProcessing(requestExecutionContext, requestParameters); //Get processed results Future<Result[]> results = getProcessedResults(requestExecutionContext, requestParameters); //do further processing //Finally, complete the request asyncHelper.onSuccess(); } catch (Exception e) { try { asyncHelper.onBizError(e.getMessage()); } catch (Exception f) { } } } @Asynchronous public void initiateProcessing(RequestExecutionContext requestExecutionContext, RequestParameters requestParameters) { //startProcessing } @Asynchronous public Future<Result[]> getProcessedResults(RequestExecutionContext requestExecutionContext, RequestParameters requestParameters) { Result[] resultsArr = null; //do processing return new AsyncResult<Result[]>(resultsArr); } }