JavaからActionScriptへのDTOおよびリモートインターフェイスの生成

JavaおよびFlexのWebアプリケーションを考えます。 Blaze DSまたはAMFシリアル化を使用する同様のテクノロジーが通信に使用されます。 サーバー側とクライアント側では、DTO(データ転送オブジェクト)とリモートサービスインターフェイスが明示的または暗黙的に存在します。 このようなアプリケーションでは、問題はクライアントとサーバー間のDTOコードの同期です。 もちろん、アプリケーションがテストで完全にカバーされている場合、テスト中にJavaソースとActionScriptソースの間の同期が外れていることが明らかになりますが、すでにコンパイル時にフィードバックを取得する機会があります。



これを行うには、ビルドするたびにDTOとリモートインターフェイスを生成する必要があります。 サーバーのDTOまたはリモートインターフェイスを変更する場合、クライアントに応じて変更されます。 これにより、テストを待たずに、コンパイル段階ですでに問題を見つけることができます。さらに、テストがソースコードを完全にカバーしていない可能性を排除することは不可能です。

Granite DSのclear toolkit 、pimento、Gas3など、AS DTOを生成するための多くのライブラリがあります。



Gas3ライブラリはflexmojosに統合されており 、テンプレートを編集してクラスを生成することができ、より適切に見えます。 さらに例では、このライブラリが使用されます。



DTO生成



DTO生成の例については、JavaクラスSampleDto





 public class SampleDto { public String field1; public String field2; public String getField1() { return field1; } public void setField1(String field1) { this.field1 = field1; } public String getField2() { return field2; } public void setField2(String field2) { this.field2 = field2; } }
      
      





このクラスをmavenモジュールgit.example.com:jar:jar



ます。

git.example.com:flex:swf



モジュールでActionScriptコードを生成します。 SampleDto.java



からASコードを生成するには、flexmojos設定に次を追加する必要があります。



 <plugin> <groupId>org.sonatype.flexmojos</groupId> <artifactId>flexmojos-maven-plugin</artifactId> <version>3.9</version> <extensions>true</extensions> <configuration> <includeJavaClasses> <includeJavaClass>com.example.*</includeJavaClass> </includeJavaClasses> </configuration> <executions> <execution> <id>generate</id> <goals> <goal>generate</goal> </goals> </execution> </executions> </plugin>
      
      





生成されたすべてのクラスは、クラス自体と祖先(基本接尾辞付き)の2つの部分で構成されます。 この場合、生成後、 SampleDtoBase



SampleDtoBase



を取得しSampleDtoBase



。 同じ名前のクラスが存在しない場合、メインクラスが生成されます。 祖先は常に生成され、古いファイルがあればそれを上書きします。 すべての追加機能は、もしあればメインクラスにのみ追加する必要があり、その場合、ジェネレーターはそれに触れません。 Baseクラスに新しい動作を追加すると、ジェネレーターはすべてを削除します。



 [Bindable] [RemoteClass(alias="com.example.SampleDto")] public class SampleDto extends SampleDtoBase { } [Bindable] public class SampleDtoBase implements IExternalizable { public var _field1:String; public var _field2:String; public function set field1(value:String):void { _field1 = value; } public function get field1():String { return _field1; } public function set field2(value:String):void { _field2 = value; } public function get field2():String { return _field2; } public function readExternal(input:IDataInput):void { _field1 = input.readObject() as String; _field2 = input.readObject() as String; } public function writeExternal(output:IDataOutput):void { output.writeObject(_field1); output.writeObject(_field2); } }
      
      





リモートインターフェイス生成



次に、リモートインターフェイスを生成します。 モジュールgit.example.com:jar:jar



javaクラスSampleService



を作成し、注釈を付けます。



 @RemoteDestination(id = "sampleService", channel = "amf") public class SampleService { public final String sampleMethod1() { return "test output"; } public final String getSampleField1(@Param("sampleDto") final SampleDto sampleDto) { return sampleDto.getField1(); } public final String getSampleField2(@Param("sampleDto") final SampleDto sampleDto) { return sampleDto.getField2(); } @IgnoredMethod public final void ignoredMethod() { } }
      
      





@RemoteDestination



は、サービスのチャネルとエンドポイント名を記述し、クラスでハングします。 @Param



は、生成されたメソッドにパラメーター名を設定しますが、オプションです。 @IgnoredMethod



は、タグ付きメソッドを無視するようジェネレーターに指示します。

生成には、DTOと同じ設定を使用します。 出力は2つのクラスです。



 [RemoteClass(alias="com.example.SampleService")] public class SampleService extends SampleServiceBase { } public class SampleServiceBase extends RemoteObject { private var _initRemote:Boolean = false; private function initRemote():void { destination = "sampleService"; channelSet = new ChannelSet(); channelSet.addChannel(ServerConfig.getChannel("amf")); _initRemote = true; } public function sampleMethod1():void { if (!_initRemote) initRemote(); getOperation("sampleMethod1").send(); } public function getSampleField2(sampleDto:SampleDto):void { if (!_initRemote) initRemote(); getOperation("getSampleField2").send(sampleDto); } public function getSampleField1(sampleDto:SampleDto):void { if (!_initRemote) initRemote(); getOperation("getSampleField1").send(sampleDto); } public function addOperationListener(op:Function, type:String, handler:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void { if (op == this.sampleMethod1) this.getOperation("sampleMethod1").addEventListener(type, handler, useCapture, priority, useWeakReference); if (op == this.getSampleField2) this.getOperation("getSampleField2").addEventListener(type, handler, useCapture, priority, useWeakReference); if (op == this.getSampleField1) this.getOperation("getSampleField1").addEventListener(type, handler, useCapture, priority, useWeakReference); } public function removeOperationListener(op:Function, event:String, handler:Function):void { if (op == this.sampleMethod1) this.getOperation("sampleMethod1").removeEventListener(event, handler); if (op == this.getSampleField2) this.getOperation("getSampleField2").removeEventListener(event, handler); if (op == this.getSampleField1) this.getOperation("getSampleField1").removeEventListener(event, handler); } }
      
      





生成テンプレート



Gas3では、生成のためにテンプレートを変更できます。 メソッドにレスポンダーをハングさせることができるように、リモートインターフェイスを生成するためのテンプレートを変更しましょう。 テンプレートはグルーヴィーで大量に書かれているため、記事には記載されていません。希望する方は、この例のリポジトリのテンプレートをこちらでご覧いただけます 。 テンプレートを変更するには、テンプレートセクションをflexmojos設定に追加します。



 <templates> <base-remote-template> ${project.basedir}/src/main/generator-templates/remoteBase.gsp </base-remote-template> </templates>
      
      





その結果、次のサービスコードを取得します。



 public class SampleServiceBase extends RemoteObject { private static const logger:ILogger = Log.getLogger(getQualifiedClassName(SampleService).replace("::", ".")); private var _initRemote:Boolean = false; public function SampleServiceBase() { super(); } private function initRemote():void { destination = "sampleService"; channelSet = new ChannelSet(); channelSet.addChannel(ServerConfig.getChannel("amf")); _initRemote = true; } public function getSampleField1(sampleDto:SampleDto, responder:IResponder = null):AsyncToken { if (!_initRemote) { initRemote(); } var asyncToken:AsyncToken = getOperation("getSampleField1").send(sampleDto); if (responder) { asyncToken.addResponder(responder); } if (Log.isDebug()) { logger.debug("Method <getSampleField1> invoked with parameters <{0}>", sampleDto); } return asyncToken; } public function sampleMethod1(responder:IResponder = null):AsyncToken { if (!_initRemote) { initRemote(); } var asyncToken:AsyncToken = getOperation("sampleMethod1").send(); if (responder) { asyncToken.addResponder(responder); } if (Log.isDebug()) { logger.debug("Method <sampleMethod1> invoked with parameters "); } return asyncToken; } public function getSampleField2(sampleDto:SampleDto, responder:IResponder = null):AsyncToken { if (!_initRemote) { initRemote(); } var asyncToken:AsyncToken = getOperation("getSampleField2").send(sampleDto); if (responder) { asyncToken.addResponder(responder); } if (Log.isDebug()) { logger.debug("Method <getSampleField2> invoked with parameters <{0}>", sampleDto); } return asyncToken; } public function addOperationListener(op:Function, type:String, handler:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void { if (op == this.getSampleField1) { this.getOperation("getSampleField1").addEventListener(type, handler, useCapture, priority, useWeakReference); } if (op == this.sampleMethod1) { this.getOperation("sampleMethod1").addEventListener(type, handler, useCapture, priority, useWeakReference); } if (op == this.getSampleField2) { this.getOperation("getSampleField2").addEventListener(type, handler, useCapture, priority, useWeakReference); } } public function removeOperationListener(op:Function, event:String, handler:Function):void { if (op == this.getSampleField1) { this.getOperation("getSampleField1").removeEventListener(event, handler); } if (op == this.sampleMethod1) { this.getOperation("sampleMethod1").removeEventListener(event, handler); } if (op == this.getSampleField2) { this.getOperation("getSampleField2").removeEventListener(event, handler); } } }
      
      





Gas3では、リモートインターフェースに加えて、Javaインターフェース、通常のBean、JPAエンティティBeanのテンプレートを変更できます。



まとめ



その結果:

  1. javaとactionscriptコードの不一致について学ぶ機会を得ました。 Javaコードが変更されると、アクションスクリプトが自動的に変更され、古いバージョンのクラスで動作するコードのすべての部分がコンパイルできなくなります。
  2. AS DTOおよびリモートインターフェイスを手動で生成する必要がなくなりました。


便宜上、これらのエンティティで動作するWebアプリケーションを作成しました。 ソースコードはこちらにあります



素材







追伸:フラッシュ/フレックスの分野の悪い傾向は知っていますが、私の経験は誰かに役立つかもしれません。



All Articles