AjaxEventBehavior スクロール もラムダで。

Wicket8 ビヘビアもラムダの恩恵が沢山あるだろう。

Wicket 7 で、SerializableConsumer が無かったおかげで例えば、AjaxEventBehavior で スクロールイベントの処理は
以下のようなクラスを用意して、

import org.apache.wicket.ajax.AjaxEventBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.danekja.java.util.function.serializable.SerializableConsumer;

/**
 * スクロール時の AjaxEventBehavior.
 */
public abstract class AjaxScrollEventBehavior extends AjaxEventBehavior{

   protected abstract void onScroll(AjaxRequestTarget target);

   public AjaxScrollEventBehavior(){
      super("scroll");
   }

   @Override
   protected void onEvent(AjaxRequestTarget target){
      onScroll(target);
   }
}

リストのスクロールに対して

listcontainer.add(new AjaxScrollEventBehavior(){
   @Override
   protected void onScroll(AjaxRequestTarget target){
      // 処理
   }
});

と書いていた。

Wicket8 で SerializableConsumer を使い、この AjaxScrollEventBehavior に、更に以下の
private コンストラクタと、static メソッドを用意すれば、ラムダが使える。

private AjaxScrollEventBehavior(String event){
   super(event);
}

public static AjaxScrollEventBehavior onScroll(SerializableConsumer<AjaxRequestTarget> c){
   return new AjaxScrollEventBehavior("scroll"){
      private static final long serialVersionUID = 1L;
      @Override
      protected void onScroll(AjaxRequestTarget target){
         c.accept(target);
      }
   };
}

たかが、2,3行の削減ではないかと言うかもしれないが、たくさん出現するとやはり、かさ張ってきて見づらくなる。

listcontainer.add(AjaxScrollEventBehavior.onScroll(t->{
    // 処理
});

Wicket7 の native Websocket を使う場合、jQuery UI 使用は注意。

Wicket7 の native Websocket を使うともう1つ、煩わしいことが見つかりました。

Websocket 接続するページのHTMLの head タグの最後に、jQuery 設定として以下のように指定されてしまうのです。

<script type="text/javascript" src="./wicket/resource/org.apache.wicket.resource.JQueryResourceReference/jquery/jquery-1.12.4-ver-1489464145840.js"></script>
<script type="text/javascript" src="./wicket/resource/org.apache.wicket.ajax.AbstractDefaultAjaxBehavior/res/js/wicket-event-jquery-ver-1489464145840.js"></script>
<script type="text/javascript" src="./wicket/resource/org.apache.wicket.ajax.AbstractDefaultAjaxBehavior/res/js/wicket-ajax-jquery-ver-1489464145840.js"></script>
<script type="text/javascript" src="./wicket/resource/org.apache.wicket.protocol.ws.api.WicketWebSocketJQueryResourceReference/res/js/wicket-websocket-jquery-ver-1489464457872.js"></script>
<script type="text/javascript" >
/*<![CDATA[*/
Wicket.Event.add(window, "domready", function(event) { 
;(function(undefined) {
   'use strict';

   if (typeof(Wicket.WebSocket.appName) === "undefined") {
      jQuery.extend(Wicket.WebSocket, { pageId: 28, resourceName: '',
         baseUrl: 'feed?28', contextPath: '/sample', appName: 'WebSocketFilter',
         filterPrefix: '' });
      Wicket.WebSocket.createDefaultConnection();
   }
})();
;
Wicket.Event.publish(Wicket.Event.Topic.AJAX_HANDLERS_BOUND);
;});
/*]]>*/
</script>
</head>

すると、Page クラスコーディング側で、jquery-2.1.4.min.js と jquery-ui-1.11.x.min.js を指定しても
head タグの最後が、上記のようにされてしまっては、任意の jQuery-UI の Widget を実行できない等の障害が起きてしまう。

仕方なく、Page の html で、ルール違反だが body タグの前に、jquery-2.1.4.min.js と jquery-ui-1.11.x.min.js の再読み込みを
記述することで、 任意の jQuery-UI の Widget をできるようにする。

<script type="text/javascript" src="/sample/js/jquery-2.1.4.min.js"></script>
<script type="text/javascript" src="/sample/js/jquery-ui-1.11.4.custom.min.js"></script>
<body>

wicket nativeでURLパターンが /wicket/* でなければならない理由は

Wicket7 の native Websocket を使う時、web.xml に設定するWebSocketFilter の URLパターンが /wicket/* でなければならない
理由は、

wicket-native-websocket-core-7.x-.jar の javascriptwicket-websocket-jquery.js で、Websocket 接続のURLプロトコル
以下のように

protocol = document.location.protocol
         .replace('https:', 'wss:')
         .replace('http:', 'ws:');

url = protocol + '//' + document.location.host + WWS.contextPath + WWS.filterPrefix + '/wicket/websocket';

となってるからである。

WebSocketFilter で、リクエストから URL から "/wicket" に該当する部分を割だして、
Websocket の URLを作るように、して欲しいものだ。

oboe2uran.hatenablog.com

Wicket 7 native Websocket の制約?!

Wicket 7 で native Websocket を使ってサーバ PUSH に近いことをしようと作っていて、気持ち悪い制約に気が付いた。

Wicket のサイト web.xml では、たいてい WebFilter として、org.apache.wicket.protocol.http.WebApplication 継承クラスのマッピングには、

   <filter-mapping>
       <filter-name>WebFilter</filter-name>
       <url-pattern>/*</url-pattern>
   </filter-mapping>

と書いてサイトURL以下全て、WebApplication を通すであろう。

Tomcat 7 の環境だと Wicket が用意する org.apache.wicket.protocol.ws.tomcat7.Tomcat7WebSocketFilter を Websocket 用のフィルタとして

   <filter>
      <filter-name>WebSocketFilter</filter-name>
      <filter-class>org.apache.wicket.protocol.ws.tomcat7.Tomcat7WebSocketFilter</filter-class>
      <init-param>
         <param-name>applicationClassName</param-name>
         <param-value>sample.SamplApplication</param-value>
      </init-param>
   </filter>

と書くであろう。
  残念はことに 2017-3-14 現在は、まだ Tomcat 8 用のこの WebSocketFilter はWicket から配布されていない。

filter-mapping にすごくとんでもないルールがあって、url-pattern は、/wicket/ で開始しないと
受信するページで、WebSocketBehavior を登録してもソケット接続コネクションが通知されず結果としてメッセージ受信できない。

   <filter-mapping>
     <filter-name>WebSocketFilter</filter-name>
     <url-pattern>/wicket/*</url-pattern>
   </filter-mapping>

と書かないとならない。
これは、BookmarkablePageLink が生成するリンクのURLと被ってしまう。
BookmarkablePageLink が生成するにのは、
サイト名 + "/wicket/bookmarkable/" + PageクラスPATH
のはずだ。

これが嫌で、filter-mapping で、 /wsocket/* のように書いてしまうと、
接続はされないで受信するページを作れない。

/wicket/* でなければいけないようである。

Wicket 8 が、もっとちゃんと進めばこんな制約はなくなるのだろうか?

Wicket 8 の AjaxButton 、Link など

Wicket 8 になるとこれまでよりもシリアライズ化された Consumer などによりラムダ記述は、
前に投稿した 
Wicket8 の LambdaModel - Oboe吹きプログラマの黙示録 の他に、

AjaxButton 、FeedBackPanel 、Link なども簡潔に書けるようになってくる。

AjaxButton には、

// onSubmit と onError の BiConsumer
onSubmit(String id, SerializableBiConsumer<AjaxButton, AjaxRequestTarget> onSubmit
  , SerializableBiConsumer<AjaxButton, AjaxRequestTarget> onError)

// あるいは、onSubmit だけ
onSubmit(String id, SerializableBiConsumer<AjaxButton, AjaxRequestTarget> onSubmit)

このおかげて、

queue(AjaxButton.onSubmit("submit", (b, t)->{
   /* onSubmit の処理 */
}, (b, t)->{
   /* onError の処理 */
});

// あるいは、
queue(AjaxButton.onSubmit("submit", (b, t)->{
   /* onSubmit の処理 */
});

と書ける。

FeedBackPanel でセットする IFeedbackMessageFilter メッセージも、setFilter は、

boolean feedbackError_flg = false;
final FeedbackPanel feedback = new FeedbackPanel("feedback");
feedback.setOutputMarkupId(true);
feedback.setFilter(e->feedbackError_flg);
queue(feedback);

と書いて

queue(AjaxButton.onSubmit("send", (b, t)->{
  feedbackError_flg = false;

  /* エラーで、feedbackError_flg を true 、error("Error message"); */
  t.add(feedback);
}));

とすることができる。

リンクの onClick も、Link.onClick で、シリアライズConsumerで、

queue(Link.onClick("bklink", k->{
   setResponsePage(HomePage.class);
}));

と書くことができる。

Consumer を Inject

Google guice で、List をインジェクションする時によく使うのが、com.google.inject.TypeLiteral 

Java8 lambda の Consumer をインジェクションすることをやれば、条件分岐によるメソッド実行を
一括管理の記述ができると思った。

まず、分岐ではなく、ただの Consumer をインジェクションする例。

Injector injector =
Guice.createInjector(new AbstractModule(){
   @Override
   protected void configure(){
      binder().bind(new TypeLiteral<Consumer<?>>(){})
      .toInstance(e->{
         System.out.println(e);
      });
   }
});

Injector で生成されるクラスのメソッドで、Consumer を実行

@Inject Consumer<String> consumer;
public void exec(){
   consumer.accept("accept! on Foo::exec()");
}


AbstractModule の configure の中で if 文を書くか、if 文による ModuleインスタンスGuice.createInjector
に渡す方法を考えるかもしれないが、
あまり整理できると思えない。

今のところ、Mapを使用することぐらいしか思いつかない。

Map<String, Consumer<String>> map = new HashMap<>();
map.put("1", e->{
   System.out.println("Test 1 : " + e);
});
map.put("2", e->{
   System.out.println("Test 2 : " + e);
});

String key = "2";

Injector injector = Guice.createInjector(new AbstractModule(){
   @Override
   protected void configure(){
      binder().bind(new TypeLiteral<Consumer<String>>(){}).toInstance(map.get(key));
   }
});

条件毎の実装クラス定義を作らなくて済むだけでもありがたいのかも知れない。

NULLインジェクション

いつも、Google guice 使用を書いていて思うのが、NULLインジェクションが発生するとき、
@Nullable をつけて、Module の configure で、toProvider を書かなくてはならないのが
煩わしい。

   binder().bind(Integer.class).toProvider(Providers.of(null));
   @Inject @Nullable private Integer i;

com.google.inject.Inject のオプション @Inject(optional=true) だけではダメで、
@Nullable が必要なのだ。
明示的であり、NULL 発生で落ちるのが解るから、toProviderと@Nullable を使うのだと言えば、それで納得なんだけど。。。

結局、optional がない javax.inject.Inject の方をいつも使っている。