AJAX の部分更新のリスタート

WicketAbstractAjaxTimerBehavior は、stop()メソッドが存在しても、start() は存在しない。
ページ全体をリフレッシュせずに、部分的に再表示を定期的に実行したり、停止したり再開する場合に、
どうしたらいいか悩んだ。
org.apache.wicket.ajax.AbstractAjaxTimerBehavior 
この中の描画判定のための stop() が実行されたかの判定メソッドisStopped() が、
final メソッドであることが AbstractAjaxTimerBehavior クラス継承を作成してリスタート機能を
作成することを困難にしている。

仕方ないので、基のAbstractDefaultAjaxBehavior から継承して用意する。

import org.apache.wicket.Component;
import org.apache.wicket.Page;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.protocol.http.WebRequest;
import org.apache.wicket.util.time.Duration;
/**
 * リスタート機能付与のAjaxTimerBehavior
 */

public class AjaxTimerBehavior extends AbstractDefaultAjaxBehavior{
   private Component component;
   private Duration updateInterval;
   private boolean stopped;
   private boolean headRendered;

   public AjaxTimerBehavior(Duration updateInterval,Component component){
      this.stopped = false;
      this.headRendered = false;
      this.component = component;
      if (updateInterval == null || updateInterval.getMilliseconds() <= 0L){
         throw new IllegalArgumentException("Invalid update interval");
      }
      this.updateInterval = updateInterval;
      return;
   }

   public final void stop(){
      this.stopped = true;
   }
   protected final void setUpdateInterval(Duration updateInterval){
      if (updateInterval == null || updateInterval.getMilliseconds() <= 0L){
         throw new IllegalArgumentException("Invalid update interval");
      }
      this.updateInterval = updateInterval;
      return;
   }
   public final Duration getUpdateInterval(){
      return this.updateInterval;
   }
   @Override
   public void renderHead(IHeaderResponse response){
      super.renderHead(response);
      WebRequest request = (WebRequest)RequestCycle.get().getRequest();
      if (!this.stopped && (!this.headRendered || !request.isAjax())){
         this.headRendered = true;
         response.renderOnLoadJavascript(getJsTimeoutCall(this.updateInterval));
      }
   }
   protected final String getJsTimeoutCall(Duration updateinterval){
      return (new StringBuilder()).append("setTimeout(\"")
.append(getCallbackScript()).append("\", ").append(updateinterval.getMilliseconds())
.append(");").toString();
   }
   @Override
   protected CharSequence getCallbackScript(){
      return generateCallbackScript*1
.append("wicketAjaxGet('").append(getCallbackUrl(onlyTargetActivePage()))
.append("'").toString());
   }
   @Override
   protected CharSequence getPreconditionScript(){
      String precondition = null;
      if(!(getComponent() instanceof Page)){
         String componentId = getComponent().getMarkupId();
         precondition = (new StringBuilder())
.append("var c = Wicket.$('").append(componentId)
.append("'); return typeof(c) != 'undefined' && c != null").toString();
      }
      return precondition;
   }
   protected boolean onlyTargetActivePage(){
      return true;
   }
   @Override
   protected final void respond(AjaxRequestTarget target){
      onTimer(target);
      if (!this.stopped)
        target.getHeaderResponse()
.renderOnLoadJavascript(getJsTimeoutCall(this.updateInterval));
   }
   public final boolean isStopped(){
      return this.stopped;
   }
   protected void onTimer(AjaxRequestTarget target){
      target.addComponent(this.component);
   }
   public final void restart(AjaxRequestTarget target){
      this.stopped = false;
      this.headRendered = false;
      onTimer(target);
      target.getHeaderResponse()
.renderOnLoadJavascript(getJsTimeoutCall(this.updateInterval));
   }

}
------ HTMLのサンプル -------------------

 form wicket:id="form"> /form>
 table>
 tr>
     td> span wicket:id="time">HH:mm:ss /span> /td>
     td> button wicket:id="switch">switch /button> /td>
 /tr>
 /table>
 div wicket:id="timer"> /div>

------- WebPageサンプル-----------------------

public class SamplePage extends WebPage{
   private AjaxTimerBehavior timebehavior;
   private boolean isActiveTimelabel;
   public TimeBehaviorPage(){
      Form Void> form = new Form Void>("form");
      add(form);
      final Label timelabel = new Label("time",new AbstractReadOnlyModel String>(){
         @Override
         public String getObject(){
            return DateUtils.dateToString(new Date(),"HH:mm:ss");
         }
      });
      timelabel.setOutputMarkupId(true);
      add(timelabel);
      this.setAjaxTimerBehavior(new AjaxTimerBehavior(Duration.seconds(1),timelabel));
      this.setActiveTimelabel(true);

      final WebMarkupContainer timer = new WebMarkupContainer("timer");
      timer.add(this.getAjaxTimerBehavior());
      add(timer);

      add(new AjaxButton("switch",form){
         @Override
         protected void onSubmit(AjaxRequestTarget target,Form ?> f){
            if (isActiveTimelabel()){
               getAjaxTimerBehavior().stop();
               setActiveTimelabel(false);
            }else{
               getAjaxTimerBehavior().restart(target);
               setActiveTimelabel(true);
            }
         }
      });
   }

   protected AjaxTimerBehavior getAjaxTimerBehavior(){
      return this.timebehavior;
   }
   protected void setAjaxTimerBehavior(AjaxTimerBehavior timebehavior){
      this.timebehavior = timebehavior;
   }

   public boolean isActiveTimelabel(){
      return this.isActiveTimelabel;
   }
   public void setActiveTimelabel(boolean isActiveTimelabel){
      this.isActiveTimelabel = isActiveTimelabel;
   }
}

*1:new StringBuilder(